始めましょう。
フラグメントを作成する
まず、 Fragment0フラグメントを作成し、onCreateView()メソッドを実装します。
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment //return inflater.inflate(R.layout.fragment1, container, false); LinearLayout linearLayout = new LinearLayout(getActivity()); linearLayout.setOrientation(LinearLayout.VERTICAL); linearLayout.setGravity(Gravity.CENTER); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); Button button = new Button(getActivity()); button.setText(""); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showFragment("jatx.networkingclassloader.dx.Fragment1", null); // } }); linearLayout.addView(button, lp); return linearLayout; }
この場合、xmlからマークアップを作成する標準的な方法は機能しないため、最初のフラグメントではプログラムで作成します。
次に、フラグメントを含むモジュールに基づいて、APKを作成し、unzipを使用してそれを解凍し、 classes.dexファイルをサーバーにアップロードする必要があります。
クラスローディングを実装します
別のモジュールで 、 NetworkingActivityクラスを作成し、次のメソッドを実装します:
@Override protected void onCreate(Bundle savedInstanceState) { // ...... dataDir = getApplicationInfo().dataDir; frameLayout = (FrameLayout) findViewById(R.id.main_frame); progressDialog = new ProgressDialog(this); progressDialog.setIndeterminate(true); progressDialog.setMessage(" "); progressDialog.show(); // classes.dex , : DownloadTask downloadTask = new DownloadTask(this, dataDir); downloadTask.execute(null, null, null); // receiver , : BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String className = intent.getStringExtra("className"); Bundle args = intent.getBundleExtra("args"); showFragment(className, args); } }; IntentFilter filter = new IntentFilter("jatx.networkingclassloader.ShowFragment"); registerReceiver(receiver, filter); } // , AsyncTask c classes.dex: public void downloadReady() { Toast.makeText(this, " ", Toast.LENGTH_SHORT).show(); progressDialog.dismiss(); showFragment("jatx.networkingclassloader.dx.Fragment0", null); } public void showFragment(String className, Bundle arguments) { // : File dexFile = new File(dataDir, "classes.dex"); Log.e("Networking activity", "Loading from dex: " + dexFile.getAbsolutePath()); // , DexClassLoader: File codeCacheDir = new File(getCacheDir() + File.separator + "codeCache"); codeCacheDir.mkdirs(); // ClassLoader: DexClassLoader dexClassLoader = new DexClassLoader( dexFile.getAbsolutePath(), codeCacheDir.getAbsolutePath(), null, getClassLoader()); try { // : Class clazz = dexClassLoader.loadClass(className); // : Fragment fragment = (Fragment) clazz.newInstance(); // : fragment.setArguments(arguments); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(R.id.main_frame, fragment); fragmentTransaction.commit(); } catch (Exception e) { e.printStackTrace(); } }
フラグメントから他のフラグメントを開く
これを行うには、クラスLoadableFragment (すべてのフラグメントのスーパークラス)で、次のメソッドを実装します。
public void showFragment(String className, Bundle args) { Intent intent = new Intent("jatx.networkingclassloader.ShowFragment"); intent.putExtra("className", className); intent.putExtra("args", args); getActivity().sendBroadcast(intent); }
ここですべてが明確であることを願っています。
次のフラグメントをわずかに異なる方法で作成しようとします。
ネットワークからxmlマークアップをロードします
開始するには、 マークアップファイルを作成してサーバーにアップロードします。 文字列からxmlレイアウトを解析できるライブラリをgithubで見つけました。 正しく動作させるには、 少しファイルを作成する必要がありました 。
したがって、 LoadableFragmentクラスに次のメソッドを追加します。
protected void loadLayoutFromURL(FrameLayout container, String url) { this.container = container; // : LayoutDownloadTask layoutDownloadTask = new LayoutDownloadTask(this, url); layoutDownloadTask.execute(null, null, null); } // , xml- : public void onLayoutDownloadSuccess(String xmlAsString) {}
これを利用して、フラグメントFragment1を作成します。
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FrameLayout frameLayout = new FrameLayout(getActivity()); loadLayoutFromURL(frameLayout, "http://tabatsky.ru/testing/fragment1.xml"); return frameLayout; } @Override public void onLayoutDownloadSuccess(String xmlAsString) { LinearLayout linearLayout = (LinearLayout) DynamicLayoutInflator.inflate(getActivity(), xmlAsString, container); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); linearLayout.setLayoutParams(lp); final EditText editText = (EditText) DynamicLayoutInflator.findViewByIdString(linearLayout, "edit_text"); Button button = (Button) DynamicLayoutInflator.findViewByIdString(linearLayout, "button"); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Bundle args = new Bundle(); args.putString("userName", editText.getText().toString()); showFragment("jatx.networkingclassloader.dx.Fragment2", args); } }); }
あとがき
プロジェクトの完全なソースコードはgithubで表示できます。 完成したAPKはここからダウンロードできます 。
そして最後に、このテクノロジーの可能なアプリケーションについていくつかお話したいと思います。たとえば、ユーザーアカウントの種類(有料/無料)に応じてサーバーから異なるclasses.dexを発行できます。これにより、アプリケーションのリバースエンジニアリングの複雑さがわずかに増加します。