手机卫士03-下载app并安装

简介: <p style="line-height:26px; text-indent:nullem; text-align:left"><span style="color:#333333"><span style="font-family:Arial">原文再续书接上一回,昨天我们把那个与服务器端的交互以及解析xml的内容给搭建出来啦,那么今天我们就来完成一下下载新版的apk并安装的逻辑写一下

原文再续书接上一回,昨天我们把那个与服务器端的交互以及解析xml的内容给搭建出来啦,那么今天我们就来完成一下下载新版的apk并安装的逻辑写一下

既然要下载apk,那么肯定是另开一个线程下载的啦,所以我们在这里就新建一个类啦

com.xiaobin.security.engine.DownloadTask


  1. <font color="#333333"><font face="Arial">package com.xiaobin.security.engine;

  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.InputStream;
  5. import java.net.HttpURLConnection;
  6. import java.net.URL;

  7. import android.app.ProgressDialog;

  8. public class DownloadTask
  9. {
  10.         
  11.         public static File getFile(String path, String filePath, ProgressDialog progressDialog) throws Exception
  12.         {
  13.                 URL url = new URL(path);
  14.                 HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
  15.                 httpURLConnection.setConnectTimeout(2000);
  16.                 httpURLConnection.setRequestMethod("GET");
  17.                 if(httpURLConnection.getResponseCode() == 200)
  18.                 {
  19.                         int total = httpURLConnection.getContentLength();
  20.                         progressDialog.setMax(total);
  21.                         
  22.                         InputStream is = httpURLConnection.getInputStream();
  23.                         File file = new File(filePath);
  24.                         FileOutputStream fos = new FileOutputStream(file);
  25.                         byte[] buffer = new byte[1024];
  26.                         int len;
  27.                         int process = 0;
  28.                         while((len = is.read(buffer)) != -1)
  29.                         {
  30.                                 fos.write(buffer, 0, len);
  31.                                 process += len;
  32.                                 progressDialog.setProgress(process);
  33.                         }
  34.                         fos.flush();
  35.                         fos.close();
  36.                         is.close();
  37.                         return file;
  38.                 }
  39.                 return null;
  40.         }

  41. }
  42. </font></font>
复制代码
好啦,那个写好了,从服务器里面拿到一个最新版的apk之后,我们就要在SplashActivity里面写一些安装的逻辑啦,还有一个内部类,用来启动另一个线程下载  com.xiaobin.security.ui.SplashActivity
  1. package com.xiaobin.security.ui;

  2. import java.io.File;

  3. import android.annotation.SuppressLint;
  4. import android.app.Activity;
  5. import android.app.AlertDialog;
  6. import android.app.ProgressDialog;
  7. import android.content.DialogInterface;
  8. import android.content.Intent;
  9. import android.content.pm.PackageInfo;
  10. import android.content.pm.PackageManager;
  11. import android.content.pm.PackageManager.NameNotFoundException;
  12. import android.net.Uri;
  13. import android.os.Bundle;
  14. import android.os.Environment;
  15. import android.os.Handler;
  16. import android.os.Message;
  17. import android.util.Log;
  18. import android.view.Window;
  19. import android.view.WindowManager;
  20. import android.view.animation.AlphaAnimation;
  21. import android.widget.LinearLayout;
  22. import android.widget.TextView;
  23. import android.widget.Toast;

  24. import com.xiaobin.security.R;
  25. import com.xiaobin.security.domain.UpdateInfo;
  26. import com.xiaobin.security.engine.DownloadTask;
  27. import com.xiaobin.security.engine.UpdateInfoService;

  28. public class SplashActivity extends Activity
  29. {
  30.         private TextView tv_version;
  31.         private LinearLayout ll;
  32.         private ProgressDialog progressDialog;
  33.         
  34.         private UpdateInfo info;
  35.         private String version;
  36.         
  37.         private static final String TAG = "Security";
  38.         
  39.         @SuppressLint("HandlerLeak")
  40.         private Handler handler = new Handler()
  41.         {
  42.                 public void handleMessage(Message msg)
  43.                 {
  44.                         if(isNeedUpdate(version))
  45.                         {
  46.                                 showUpdateDialog();
  47.                         }
  48.                 };
  49.         };
  50.         
  51.         @Override
  52.         protected void onCreate(Bundle savedInstanceState)
  53.         {
  54.                 super.onCreate(savedInstanceState);
  55.                 requestWindowFeature(Window.FEATURE_NO_TITLE);
  56.                 setContentView(R.layout.splash);
  57.                 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
  58.                
  59.                 tv_version = (TextView) findViewById(R.id.tv_splash_version);
  60.                 version = getVersion();
  61.                 tv_version.setText("版本号  " + version);
  62.                
  63.                 ll = (LinearLayout) findViewById(R.id.ll_splash_main);
  64.                 AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
  65.                 alphaAnimation.setDuration(2000);
  66.                 ll.startAnimation(alphaAnimation);
  67.                
  68.                 progressDialog = new ProgressDialog(this);
  69.                 progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  70.                 progressDialog.setMessage("正在下载...");
  71.                
  72.                 new Thread()
  73.                 {
  74.                         public void run()
  75.                         {
  76.                                 try
  77.                                 {
  78.                                         sleep(3000);
  79.                                         handler.sendEmptyMessage(0);
  80.                                 }
  81.                                 catch (InterruptedException e)
  82.                                 {
  83.                                         e.printStackTrace();
  84.                                 }
  85.                         };
  86.                 }.start();
  87.                
  88.         }
  89.         
  90.         private void showUpdateDialog()
  91.         {
  92.                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
  93.                 builder.setIcon(android.R.drawable.ic_dialog_info);
  94.                 builder.setTitle("升级提醒");
  95.                 builder.setMessage(info.getDescription());
  96.                 builder.setCancelable(false);
  97.                
  98.                 builder.setPositiveButton("确定", new DialogInterface.OnClickListener()
  99.                 {
  100.                         
  101.                         @Override
  102.                         public void onClick(DialogInterface dialog, int which)
  103.                         {
  104.                                 if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
  105.                                 {
  106.                                         File dir = new File(Environment.getExternalStorageDirectory(), "/security/update");
  107.                                         if(!dir.exists())
  108.                                         {
  109.                                                 dir.mkdirs();
  110.                                         }
  111.                                         String apkPath = Environment.getExternalStorageDirectory() + "/security/update/new.apk";
  112.                                         UpdateTask task = new UpdateTask(info.getUrl(), apkPath);
  113.                                         progressDialog.show();
  114.                                         new Thread(task).start();
  115.                                 }
  116.                                 else
  117.                                 {
  118.                                         Toast.makeText(SplashActivity.this, "SD卡不可用,请插入SD卡", Toast.LENGTH_SHORT).show();
  119.                                         loadMainUI();
  120.                                 }
  121.                         }
  122.                 });
  123.                 builder.setNegativeButton("取消", new DialogInterface.OnClickListener()
  124.                 {

  125.                         @Override
  126.                         public void onClick(DialogInterface dialog, int which)
  127.                         {
  128.                                 loadMainUI();
  129.                         }
  130.                         
  131.                 });
  132.                 builder.create().show();
  133.         }

  134.         private boolean isNeedUpdate(String version)
  135.         {
  136.                 UpdateInfoService updateInfoService = new UpdateInfoService(this);
  137.                 try
  138.                 {
  139.                         info = updateInfoService.getUpdateInfo(R.string.serverUrl);
  140.                         String v = info.getVersion();
  141.                         if(v.equals(version))
  142.                         {
  143.                                 Log.i(TAG, "当前版本:" + version);
  144.                                 Log.i(TAG, "最新版本:" + v);
  145.                                 loadMainUI();
  146.                                 return false;
  147.                         }
  148.                         else
  149.                         {
  150.                                 Log.i(TAG, "需要更新");
  151.                                 return true;
  152.                         }
  153.                 }
  154.                 catch (Exception e)
  155.                 {
  156.                         e.printStackTrace();
  157.                         Toast.makeText(this, "获取更新信息异常,请稍后再试", Toast.LENGTH_SHORT).show();
  158.                         loadMainUI();
  159.                 }
  160.                 return false;
  161.         }

  162.         private String getVersion()
  163.         {
  164.                 try
  165.                 {
  166.                         PackageManager packageManager = getPackageManager();
  167.                         PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
  168.                         
  169.                         return packageInfo.versionName;
  170.                 }
  171.                 catch (NameNotFoundException e)
  172.                 {
  173.                         e.printStackTrace();
  174.                         return "版本号未知";
  175.                 }
  176.         }
  177.         
  178.         private void loadMainUI()
  179.         {
  180.                 Intent intent = new Intent(this, MainActivity.class);
  181.                 startActivity(intent);
  182.                 finish();
  183.         }
  184.         
  185.         /**
  186.          * 安装apk
  187.          * @param file 要安装的apk的目录
  188.          */
  189.         private void install(File file)
  190.         {
  191.                 Intent intent = new Intent();
  192.                 intent.setAction(Intent.ACTION_VIEW);
  193.                 intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
  194.                 finish();
  195.                 startActivity(intent);
  196.         }
  197.         
  198.         //===========================================================================================
  199.         
  200.         /**
  201.          * 下载的线程
  202.          *
  203.          */
  204.         class UpdateTask implements Runnable
  205.         {
  206.                 private String path;
  207.                 private String filePath;
  208.                
  209.                 public UpdateTask(String path, String filePath)
  210.                 {
  211.                         this.path = path;
  212.                         this.filePath = filePath;
  213.                 }

  214.                 @Override
  215.                 public void run()
  216.                 {
  217.                         try
  218.                         {
  219.                                 File file = DownloadTask.getFile(path, filePath, progressDialog);
  220.                                 progressDialog.dismiss();
  221.                                 install(file);
  222.                         }
  223.                         catch (Exception e)
  224.                         {
  225.                                 e.printStackTrace();
  226.                                 progressDialog.dismiss();
  227.                                 Toast.makeText(SplashActivity.this, "更新失败", Toast.LENGTH_SHORT).show();
  228.                                 loadMainUI();
  229.                         }
  230.                 }
  231.                
  232.         }

  233. }

复制代码
ps:上面那个com.xiaobin.security.ui.SplashActivity类里面的那个handler是我为让用户清楚看到那个启动界面而设置的,不然,因为都是在同一个局域网里面,一下子就会完成那个更新的判断的啦,如果不用更新,那就会看不到那个启动界面的啦,所以为了我们能够看到那个界面,我让它休眠了2秒钟,才进行与服务器更新的,所以有什么不明白的也可以问一下
就这样子,我们就把启动界面时候,从服务器获取最新版的内容,然后提示用户是不是要更新的处理弄好啦! 既然启动界面弄好啦,那么,我们接下来,肯定是要做我们最重要的主界面啦 我们的主界面用到的是一个叫GridView的控件,它需要一个adapter,所以我们也要新建一个adapter的类 下面是主界面的布局文件
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     android:orientation="vertical" >
  6.    
  7.     <LinearLayout
  8.         android:layout_width="match_parent"
  9.         android:layout_height="40dip"
  10.         android:gravity="center_vertical|center_horizontal"
  11.         android:background="@drawable/title_background"
  12.         android:orientation="vertical">
  13.         
  14.         <TextView
  15.             android:layout_width="wrap_content"
  16.             android:layout_height="wrap_content"
  17.             android:textColor="@android:color/white"
  18.             android:textSize="22sp"
  19.             android:text="@string/main"/>
  20.         
  21.     </LinearLayout>
  22.    
  23.     <GridView
  24.         android:id="@+id/gv_main"
  25.         android:layout_width="match_parent"
  26.         android:layout_height="match_parent"
  27.         android:verticalSpacing="8dip"
  28.         android:numColumns="2" />

  29. </LinearLayout>

复制代码
下面是adapter类的代码com.xiaobin.security.adapter.MainUIAdapter
  1. package com.xiaobin.security.adapter;

  2. import android.content.Context;
  3. import android.content.SharedPreferences;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.ViewGroup;
  7. import android.widget.BaseAdapter;
  8. import android.widget.ImageView;
  9. import android.widget.TextView;

  10. import com.xiaobin.security.R;

  11. public class MainUIAdapter extends BaseAdapter
  12. {
  13.         private static final String[] NAMES = new String[] {"手机防盗", "通讯卫士", "软件管理", "流量管理", "任务管理", "手机杀毒",
  14.                         "系统优化", "高级工具", "设置中心"};
  15.         
  16.         private static final int[] ICONS = new int[] {R.drawable.widget01, R.drawable.widget02, R.drawable.widget03,
  17.                         R.drawable.widget04, R.drawable.widget05, R.drawable.widget06, R.drawable.widget07,
  18.                         R.drawable.widget08, R.drawable.widget09};
  19.         
  20.         //声明成静态,起到一定的优化作用,关于adapter还有别的优化方法的,有机会我们再说
  21.         private static ImageView imageView;
  22.         private static TextView textView;
  23.         
  24.         private Context context;
  25.         private LayoutInflater inflater;
  26.         private SharedPreferences sp;
  27.         
  28.         public MainUIAdapter(Context context)
  29.         {
  30.                 this.context = context;
  31.                 inflater = LayoutInflater.from(this.context);
  32.                 sp = context.getSharedPreferences("config", Context.MODE_PRIVATE);
  33.         }

  34.         @Override
  35.         public int getCount()
  36.         {
  37.                 return NAMES.length;
  38.         }

  39.         @Override
  40.         public Object getItem(int position)
  41.         {
  42.                 return position;
  43.         }

  44.         @Override
  45.         public long getItemId(int position)
  46.         {
  47.                 return position;
  48.         }

  49.         @Override
  50.         public View getView(int position, View convertView, ViewGroup parent)
  51.         {
  52.                 View view = inflater.inflate(R.layout.main_item, null);
  53.                 imageView = (ImageView) view.findViewById(R.id.iv_main_icon);
  54.                 textView = (TextView) view.findViewById(R.id.tv_main_name);
  55.                 imageView.setImageResource(ICONS[position]);
  56.                 textView.setText(NAMES[position]);
  57.                         
  58.                 if(position == 0)
  59.                 {
  60.                         String name = sp.getString("lostName", "");
  61.                         if(!name.equals(""))
  62.                         {
  63.                                 textView.setText(name);
  64.                         }
  65.                 }
  66.                
  67.                 return view;
  68.         }
  69.         
  70. }

复制代码
adapter也写好啦,那么接下来,肯定就是我们的主界面啦com.xiaobin.security.ui.MainActivity
  1. package com.xiaobin.security.ui;

  2. import com.xiaobin.security.R;
  3. import com.xiaobin.security.adapter.MainUIAdapter;

  4. import android.app.Activity;
  5. import android.app.AlertDialog;
  6. import android.content.Context;
  7. import android.content.DialogInterface;
  8. import android.content.SharedPreferences;
  9. import android.content.SharedPreferences.Editor;
  10. import android.os.Bundle;
  11. import android.view.View;
  12. import android.widget.AdapterView;
  13. import android.widget.EditText;
  14. import android.widget.TextView;
  15. import android.widget.Toast;
  16. import android.widget.AdapterView.OnItemLongClickListener;
  17. import android.widget.GridView;
  18. import android.widget.AdapterView.OnItemClickListener;

  19. public class MainActivity extends Activity implements OnItemClickListener
  20. {
  21.         private GridView gridView;
  22.         
  23.         private MainUIAdapter adapter ;
  24.         private SharedPreferences sp;

  25.         @Override
  26.         protected void onCreate(Bundle savedInstanceState)
  27.         {
  28.                 super.onCreate(savedInstanceState);
  29.                 setContentView(R.layout.main);
  30.                
  31.                 sp = this.getSharedPreferences("config", Context.MODE_PRIVATE);
  32.                 gridView = (GridView) findViewById(R.id.gv_main);
  33.                 adapter = new MainUIAdapter(this);
  34.                 gridView.setAdapter(adapter);
  35.                 gridView.setOnItemClickListener(this);
  36.                 gridView.setOnItemLongClickListener(new OnItemLongClickListener()
  37.                 {
  38.                         @Override
  39.                         public boolean onItemLongClick(AdapterView<?> parent, final View view, int position, long id)
  40.                         {
  41.                                 if(position == 0)        //这个是因为,如果我们的手机被盗了,用户一看到第一个手机防盗,那样肯定会先卸载我们的程序的,所以我们在手机防盗这个item里面,设置了一个重命名的功能
  42.                                 {
  43.                                         AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
  44.                                         builder.setTitle("设置");
  45.                                         builder.setMessage("请输入要理性的名称");
  46.                                         final EditText et = new EditText(MainActivity.this);
  47.                                         et.setHint("新名称");
  48.                                         builder.setView(et);
  49.                                         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
  50.                                         {
  51.                                                 @Override
  52.                                                 public void onClick(DialogInterface dialog, int which)
  53.                                                 {
  54.                                                         String name = et.getText().toString();
  55.                                                         if(name.equals(""))
  56.                                                         {
  57.                                                                 Toast.makeText(MainActivity.this, "输入内容不能为空", Toast.LENGTH_SHORT).show();
  58.                                                         }
  59.                                                         else
  60.                                                         {
  61.                                                                 Editor editor = sp.edit();
  62.                                                                 editor.putString("lostName", name);
  63.                                                                 editor.commit();
  64.                                                                
  65.                                                                 TextView tv = (TextView) view.findViewById(R.id.tv_main_name);
  66.                                                                 tv.setText(name);
  67.                                                                 adapter.notifyDataSetChanged();
  68.                                                         }
  69.                                                 }
  70.                                         });
  71.                                         builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
  72.                                         {
  73.                                                 @Override
  74.                                                 public void onClick(DialogInterface dialog, int which)
  75.                                                 {
  76.                                                         // TODO Auto-generated method stub
  77.                                                         
  78.                                                 }
  79.                                         });
  80.                                         builder.create().show();
  81.                                 }
  82.                                 return false;
  83.                         }
  84.                 });
  85.         }

  86.         @Override
  87.         public void onItemClick(AdapterView<?> parent, View view, int position, long id)
  88.         {
  89.                 switch(position)
  90.                 {
  91.                         case 0 : //手机防盗
  92.                                 break;
  93.                                 
  94.                         case 1 : //通讯卫士
  95.                                 break;
  96.                                 
  97.                         case 2 : //软件管理
  98.                                 break;
  99.                                 
  100.                         case 3 : //流量管理
  101.                                 break;
  102.                                 
  103.                         case 4 : //任务管理
  104.                                 break;
  105.                                 
  106.                         case 5 : //手机杀毒
  107.                                 break;
  108.                                 
  109.                         case 6 : //系统优化
  110.                                 break;
  111.                                 
  112.                         case 7 : //高级工具
  113.                                 break;
  114.                                 
  115.                         case 8 : //设置中心
  116.                                 break;
  117.                                 
  118.                         default :
  119.                                 break;
  120.                 }
  121.         }

  122. }

复制代码
上面的那个重命名的功能,是我们把重新命名的名字,存放到一个SharedPreferences里面的,然后下一次启动的时候,就先检查里面有没有对应的值,没有就用默认的,有就用用户自己命名的 好啦,今天的代码基本上写得差不多的啦,现在只剩下添加权限啦,因为要读取sd卡,所以要添加相应的权限
  1. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
  2.     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
复制代码
现在就可以测试啦,记得要把服务器打开喔,还要昨天说的那个服务器的目录下面放一个apk喔,并把那个update.xml修改成与现在这个版本不一致的喔,不然不会更新的,不明白的可以看看下面的图 注意一下名称的对应 好啦,今天就说到这里啦 Security_03_完成app更新的逻辑和主界面.rar(1.67 MB, 下载次数: 1115)
目录
相关文章
|
2月前
|
小程序 JavaScript API
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
这篇文章介绍了如何在uni-app和微信小程序中实现将图片保存到用户手机相册的功能。
650 0
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
|
3月前
|
移动开发 Android开发 数据安全/隐私保护
移动应用与系统的技术演进:从开发到操作系统的全景解析随着智能手机和平板电脑的普及,移动应用(App)已成为人们日常生活中不可或缺的一部分。无论是社交、娱乐、购物还是办公,移动应用都扮演着重要的角色。而支撑这些应用运行的,正是功能强大且复杂的移动操作系统。本文将深入探讨移动应用的开发过程及其背后的操作系统机制,揭示这一领域的技术演进。
本文旨在提供关于移动应用与系统技术的全面概述,涵盖移动应用的开发生命周期、主要移动操作系统的特点以及它们之间的竞争关系。我们将探讨如何高效地开发移动应用,并分析iOS和Android两大主流操作系统的技术优势与局限。同时,本文还将讨论跨平台解决方案的兴起及其对移动开发领域的影响。通过这篇技术性文章,读者将获得对移动应用开发及操作系统深层理解的钥匙。
|
2月前
|
缓存 开发框架 移动开发
uni-app:下载使用uni&创建项目&和小程序链接&数据缓存&小程序打包 (一)
uni-app 是一个跨平台的开发框架,它允许开发者使用 Vue.js 来构建应用程序,并能够同时发布到多个平台,如微信小程序、支付宝小程序、H5、App(通过DCloud的打包服务)等。uni-app 的目标是通过统一的代码库,简化多平台开发过程,提高开发效率。 在这一部分中,我们将逐步介绍如何下载和使用uni-app、创建一个新的项目、如何将项目链接到小程序,以及实现数据缓存的基本方法。
|
4月前
|
JSON Linux 网络安全
【Azure 应用服务】如何从App Service for Linux 的环境中下载Container中非Home目录下的文件呢?
【Azure 应用服务】如何从App Service for Linux 的环境中下载Container中非Home目录下的文件呢?
|
4月前
|
API 网络架构
【Azure Logic App】在中国区的微软云服务上,使用逻辑应用是否可以下载SharePoint上的文件呢?
【Azure Logic App】在中国区的微软云服务上,使用逻辑应用是否可以下载SharePoint上的文件呢?
【Azure Logic App】在中国区的微软云服务上,使用逻辑应用是否可以下载SharePoint上的文件呢?
|
4月前
|
存储 NoSQL Java
使用redis进行手机验证码的验证、每天只能发送三次验证码 (redis安装在虚拟机linux系统中)
该博客文章展示了如何在Linux虚拟机上使用Redis和Jedis客户端实现手机验证码的验证功能,包括验证码的生成、存储、验证以及限制每天发送次数的逻辑,并提供了测试结果截图。
使用redis进行手机验证码的验证、每天只能发送三次验证码 (redis安装在虚拟机linux系统中)
|
4月前
|
Web App开发 iOS开发
【Azure 应用服务】App Service站点Header头中的中文信息显示乱码?当下载文件时,文件名也是乱码?
【Azure 应用服务】App Service站点Header头中的中文信息显示乱码?当下载文件时,文件名也是乱码?
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的多功能智能手机阅读APP的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的多功能智能手机阅读APP的详细设计和实现(源码+lw+部署文档+讲解等)
|
5月前
|
存储 移动开发 Android开发
使用kotlin Jetpack Compose框架开发安卓app, webview中h5如何访问手机存储上传文件
在Kotlin和Jetpack Compose中,集成WebView以支持HTML5页面访问手机存储及上传音频文件涉及关键步骤:1) 添加`READ_EXTERNAL_STORAGE`和`WRITE_EXTERNAL_STORAGE`权限,考虑Android 11的分区存储;2) 配置WebView允许JavaScript和文件访问,启用`javaScriptEnabled`、`allowFileAccess`等设置;3) HTML5页面使用`<input type="file">`让用户选择文件,利用File API;
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的多功能智能手机阅读APP附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的多功能智能手机阅读APP附带文章源码部署视频讲解等
79 1

热门文章

最新文章

下一篇
无影云桌面