Android:ネットワークからフラグメントを動的にロードします

この記事では、プログラムの実行中にネットワークからクラス(フラグメントを含む)をダウンロードし、それらをAndroidアプリケーションで使用する方法を見ていきます。 このような技術の実際の応用分野は会話の別のトピックですが、この機能の実装自体は私にとって非常に興味深い仕事のように思えました。



始めましょう。



フラグメントを作成する



まず、 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を発行できます。これにより、アプリケーションのリバースエンジニアリングの複雑さがわずかに増加します。



All Articles