Android使用Service实现后台更新并安装

         当谈到在Android应用中实现后台更新并安装时,使用Service是一种强大的方法。通过Service,您可以在应用处于后台或者甚至是关闭状态时执行更新操作,这为用户提供了无缝的体验,同时也确保了应用始终保持最新版本。

  1. 实现后台更新:使用Service可以在后台执行更新任务,而不会打断用户正在进行的操作。这种无感知的更新方式可以提升用户体验,避免了在更新时弹出窗口或者干扰用户的使用。

  2. 灵活的更新策略:通过Service,您可以灵活地控制更新策略。例如,可以在应用启动时检查更新,或者通过定时任务在后台自动检查更新,以确保用户始终使用最新版本的应用程序。

  3. 安全更新:借助Service,您可以确保应用更新的安全性。通过使用加密技术或者验证机制,您可以防止恶意软件篡改应用更新,从而保护用户的设备和数据安全。

  4. 无需用户干预的安装:使用Service进行更新后,应用可以自动下载更新文件并完成安装,无需用户手动干预。这种自动化的更新过程节省了用户的时间和精力,提高了更新的便捷性。

  5. 用户通知和反馈:在更新过程中,您可以利用Service发送通知,向用户展示更新进度或者更新完成的提示。此外,还可以通过Service收集用户反馈或者更新日志,以改进后续版本的更新策略。

  6. 话不多说先上图:

        要实现这样的效果总体来说还是比较简单的,代码如下:

        MainActivity.java

public class MainActivity extends AppCompatActivity {

    public static String title="新版本2.0"
            ,content="测试内容"
            ,url="http://files.diablos.cn/2024/03/10/18fbba9e10ee43acb432bfcb19044c13.jpg";

    // 请求权限的请求代码
    private static final int PERMISSION_REQUEST_CODE = 100;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        checkAndRequestPermissions();
        Button button=findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog();
            }
        });
    }

    private void dialog(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(title);
        builder.setMessage(content);
        builder.setPositiveButton("更新", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                // 当用户点击对话框上的确定按钮时执行的操作
                //启动service后台服务
                Intent serviceIntent = new Intent(MainActivity.this, MyService.class);
                startService(serviceIntent);
            }
        });
        builder.setNegativeButton("忽略", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

                dialog.dismiss();
            }
        });
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    // 检查并请求所需权限
    private void checkAndRequestPermissions() {
        // 检查通知权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) {
                // 如果通知权限未启用,引导用户到设置页面启用
                Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
                intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
                startActivity(intent);
            }
        }

    }

    // 处理权限请求的结果
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限请求成功
                Toast.makeText(this, "Permission granted.", Toast.LENGTH_SHORT).show();
            } else {
                // 权限请求失败
                Toast.makeText(this, "Permission denied.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

        下载类 Download.java

public class Download {
    // 下载回调接口
    ondown dow;
    // 下载状态信息
    String ruest;
    // 文件总长度和当前已下载长度
    int length=0, len=0;

    // 下载回调接口定义
    public static interface ondown {
        // 下载中回调方法,参数为当前已下载长度和文件总长度
        public void downing(int len, int oklen);
        // 下载完成回调方法,参数为下载状态信息
        public void downok(String ruest);
    }

    // 设置下载回调接口
    public void setondown(ondown dow) {
        this.dow = dow;
    }

    // 下载文件方法
    public void dowmFile(final String URL, final String path) {
        new Thread() {
            public void run() {
                String url=URL;
                try {
                    // 创建目标文件路径
                    File ff=new File(path);
                    if (!ff.getParentFile().exists()) {
                        ff.getParentFile().mkdirs();
                    }
                    // 对URL进行编码处理
                    String rgx="(?<==)(.|\n)+?(?=&|$)";
                    Matcher su= Pattern.compile(rgx).matcher(url);
                    while (su.find()) {
                        String o=su.group();
                        url = url.replace(o, URLEncoder.encode(o));
                    }
                    String[] one={"{","}"};
                    for (String two:one) {
                        url = url.replace(two, URLEncoder.encode(two));
                    }
                    // 打开URL连接并获取输入流
                    URL UR=new URL(url);
                    URLConnection op=UR.openConnection();
                    InputStream input=op.getInputStream();
                    byte[] b=new byte[1024 * 1024];
                    int le=0;
                    // 获取文件总长度
                    length = op.getContentLength();
                    len = 0;
                    // 创建输出流,写入文件
                    FileOutputStream hj=new FileOutputStream(path);
                    while ((le = input.read(b)) != -1) {
                        len += le;
                        // 更新下载进度
                        if (dow != null) {
                            Message me=new Message();
                            me.what = 0;
                            hh.sendMessage(me);
                        }
                        hj.write(b, 0, le);
                    }
                    input.close();
                    hj.flush();
                    hj.close();
                    ruest = "下载完成";
                } catch (Exception e) {
                    ruest = e.toString();
                }
                // 发送下载完成消息
                Message me=new Message();
                me.what = 1;
                hh.sendMessage(me);
            }
        }.start();
    }

    // 处理下载进度和下载完成消息的Handler
    Handler hh=new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    // 调用回调接口,通知下载进度
                    dow.downing(length, len);
                    break;
                case 1:
                    // 调用回调接口,通知下载完成
                    dow.downok(ruest);
                    break;
            }
        }
    };
}

        然后最重要的部分MyService.java

public class MyService extends Service {

    private static final String TAG = "MyService";
    private static final int notificationid = 100001;


    private Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            ifapk();//判断apk是否已经下载
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "Service created");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "Service started");

        // 在示例中,仅模拟任务执行
        new Thread() {
            @Override
            public void run() {
                super.run();
                Message msg = new Message();
                handler.sendMessage(msg);
                stopSelf(); // 任务完成后停止Service
            }
        }.start();

        return START_STICKY; // 标记Service被系统关闭后自动重启
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Service destroyed");
        // 取消通知栏
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null; // 此处不需要绑定Service,因此返回null
    }

    /**
     * 判断是否已经下载
     */
    private void ifapk(){
        File apkfile = new File(getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + "/" + title + ".apk");
        if(apkfile.exists()){
            //已经下载,直接安装
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//安装完成后打开新版本
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 给目标应用一个临时授权
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//判断版本大于等于7.0
                //如果SDK版本>=24,即:Build.VERSION.SDK_INT >= 24,使用FileProvider兼容安装apk
                String packageName = getApplicationContext().getApplicationContext().getPackageName();
                String authority = new StringBuilder(packageName).append(".fileProvider").toString();
                Uri apkUri = FileProvider.getUriForFile(this, authority, apkfile);
                intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            } else {
                intent.setDataAndType(Uri.fromFile(apkfile), "application/vnd.android.package-archive");
            }
            getApplicationContext().startActivity(intent);
            android.os.Process.killProcess(android.os.Process.myPid());//安装完之后会提示”完成” “打开”。
        }else {
            //未下载
            sendNotification(title,content);//发送通知栏
            downloadapk();//下载
        }
    }

    /**
     * 下载apk
     */
    private void downloadapk(){
        Download down = new Download();
        down.setondown(new Download.ondown() {
            @Override
            public void downing(int len, int oklen) {
                //下载ing
                updateNotificationProgress((int) (((double) oklen / (double) len) * 100));
            }
            @Override
            public void downok(String ruest) {
                //下载完成判断apk
                ifapk();
            }
        });

        down.dowmFile(url, getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + "/" + title + ".apk");///mnt/sdcard
    }

    /**
     * 更新通知栏
     * @param title
     * @param message
     */
    public void sendNotification(String title,String message) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE);
//        // 1. 创建一个通知(必须设置channelId)
        Notification notification = new Notification.Builder(this, notificationid + "")
                .setContentTitle(title)
                .setContentText(message)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                //.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),R.drawable.cjss)
                .setContentIntent(pendingIntent) // 设置点击通知时的操作

                .setAutoCancel(true)
                .build();
        // 2. 获取系统的通知管理器
        android.app.NotificationManager notificationManager = (android.app.NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        // 3. 创建NotificationChannel(这里传入的channelId要和创建的通知channelId一致,才能为指定通知建立通知渠道)
        NotificationChannel channel = new NotificationChannel(notificationid+"","测试渠道名称", android.app.NotificationManager.IMPORTANCE_DEFAULT);
        notificationManager.createNotificationChannel(channel);
        // 4. 发送通知
        notificationManager.notify(notificationid, notification);
    }

    /**
     * 更新下载进度
     * @param progress
     */
    private void updateNotificationProgress(int progress) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE);

        Notification notification = new Notification.Builder(this, notificationid + "")
                .setContentTitle(title)
                .setContentText("下载进度:" + progress + "%")
                .setProgress(100, progress, false)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                //.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),R.drawable.cjss)
                .setContentIntent(pendingIntent) // 设置点击通知时的操作
                .setAutoCancel(true)
                .build();
        // 2. 获取系统的通知管理器
        android.app.NotificationManager notificationManager = (android.app.NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        // 3. 创建NotificationChannel(这里传入的channelId要和创建的通知channelId一致,才能为指定通知建立通知渠道)
        NotificationChannel channel = new NotificationChannel(notificationid+"","测试渠道名称", android.app.NotificationManager.IMPORTANCE_DEFAULT);
        notificationManager.createNotificationChannel(channel);


        // 发送更新后的通知
        notificationManager.notify(notificationid, notification);
    }

}

        主要代码基本就是上面这些啦,但是还有一点点的配置文件也是很重要的。

        (res/xml/)network.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

        (res/xml/)filepath.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!--安装包文件存储路径-->
    <external-files-path
        name="my_download"
        path="Download" />
    <external-path
        name="."
        path="." />
</paths>

        最后就是AndroiManifest.xml

        相关权限

<!-- 允许联网 -->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- 开机自启 -->
    <uses-permission android:name="android.permission.VIBRATE" /> <!-- 通知栏 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" /> <!-- 获取GSM(2g)、WCDMA(联通3g)等网络状态的信息 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 获取wifi网络状态的信息 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 保持CPU 运转,屏幕和键盘灯有可能是关闭的,用于文件上传和下载 -->
    <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 获取sd卡写的权限,用于文件上传和下载 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 允许读取手机状态 用于创建BmobInstallation -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 安装APK权限 -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <uses-permission android:name="android.permission.REPLACE_EXISTING_PACKAGE" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /><!--安卓13通知-->

        其中权限部分不用说了吧,主要是 android:networkSecurityConfig="@xml/network"                                                                                      和android:requestLegacyExternalStorage="true"用于安卓应用程序的网络安全配置文件(直接一点就是访问能访问http协议)

        以及元数据部分

        <meta-data
            android:name="com.google.android.actions"
            android:resource="@xml/filepath" />

        最后是最最重要的别忘记申明 MyService 类作为应用程序的服务组件!

        - android:enabled: 表示系统是否可以实例化并运行服务。这里设置为 "true",允许系统根据需要启动服务。

        - android:exported: 指定服务是否可以供其他应用程序的组件访问和交互。将其设置为 "true" 表示服务可以被其他应用程序的组件访问。

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

        (需要配置的地方有3处我就直接整个贴出来了)

        AndroiManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- 允许联网 -->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- 开机自启 -->
    <uses-permission android:name="android.permission.VIBRATE" /> <!-- 通知栏 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" /> <!-- 获取GSM(2g)、WCDMA(联通3g)等网络状态的信息 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 获取wifi网络状态的信息 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 保持CPU 运转,屏幕和键盘灯有可能是关闭的,用于文件上传和下载 -->
    <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 获取sd卡写的权限,用于文件上传和下载 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 允许读取手机状态 用于创建BmobInstallation -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 安装APK权限 -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <uses-permission android:name="android.permission.REPLACE_EXISTING_PACKAGE" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /><!--安卓13通知-->


    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network"
        android:requestLegacyExternalStorage="true"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication"
        tools:targetApi="31">

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

        <meta-data
            android:name="com.google.android.actions"
            android:resource="@xml/filepath" />

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

        好了到这里就基本实现了app更新迭代的功能了,有任何问题欢迎在评论区指出。

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大菠萝‍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值