溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android實現(xiàn)APP自動更新功能的方法

發(fā)布時間:2021-04-17 11:03:43 來源:億速云 閱讀:956 作者:小新 欄目:移動開發(fā)

這篇文章將為大家詳細講解有關(guān)Android實現(xiàn)APP自動更新功能的方法,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

現(xiàn)在一般的android軟件都是需要不斷更新的,當你打開某個app的時候,如果有新的版本,它會提示你有新版本需要更新。該小程序?qū)崿F(xiàn)的就是這個功能。

該小程序的特點是,當有更新時,會彈出一個提示框,點擊確定,則在通知來創(chuàng)建一個進度條進行下載,點擊取消,則取消更新。

以下是詳細代碼:

1.創(chuàng)建布局文件notification_item.xml,用于在通知欄生成一個進度條和下載圖標。

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="fill_parent" 
 android:layout_height="fill_parent" 
 android:padding="3dp" > 
 
 <ImageView 
  android:id="@+id/notificationImage" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:src="@android:drawable/stat_sys_download" /> 
 
 <TextView 
  android:id="@+id/notificationTitle" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignParentRight="true" 
  android:layout_toRightOf="@id/notificationImage" 
  android:paddingLeft="6dp" 
  android:textColor="#FF000000" /> 
 
 <TextView 
  android:id="@+id/notificationPercent" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_below="@id/notificationImage" 
  android:paddingTop="2dp" 
  android:textColor="#FF000000" /> 
 
 <ProgressBar 
  android:id="@+id/notificationProgress" 
   
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignLeft="@id/notificationTitle" 
  android:layout_alignParentRight="true" 
  android:layout_alignTop="@id/notificationPercent" 
  android:layout_below="@id/notificationTitle" 
  android:paddingLeft="6dp" 
  android:paddingRight="3dp" 
  android:paddingTop="2dp" /> 
 
</RelativeLayout>

2.創(chuàng)建AppContext類,該類繼承自Application。

package com.test.application; 
 
import android.app.Application; 
import android.content.Context; 
 
import com.test.update.config.Config; 
 
public class AppContext extends Application { 
 private static AppContext appInstance; 
 private Context context; 
 
 public static AppContext getInstance() { 
  return appInstance; 
 } 
 
 @Override 
 public void onCreate() { 
  // TODO Auto-generated method stub 
  super.onCreate(); 
  appInstance = this; 
  context = this.getBaseContext(); 
//  // 獲取當前版本號 
//  try { 
//   PackageInfo packageInfo = getApplicationContext() 
//     .getPackageManager().getPackageInfo(getPackageName(), 0); 
//   Config.localVersion = packageInfo.versionCode; 
//   Config.serverVersion = 1;// 假定服務(wù)器版本為2,本地版本默認是1 
//  } catch (NameNotFoundException e) { 
//   e.printStackTrace(); 
//  } 
  initGlobal(); 
 } 
 
 public void initGlobal() { 
  try { 
   Config.localVersion = getPackageManager().getPackageInfo( 
     getPackageName(), 0).versionCode; // 設(shè)置本地版本號 
   Config.serverVersion = 2;// 假定服務(wù)器版本為2,本地版本默認是1--實際開發(fā)中是從服務(wù)器獲取最新版本號,android具體與后端的交互見我另///外的博文 
  } catch (Exception ex) { 
   ex.printStackTrace(); 
  } 
 } 
}

3.創(chuàng)建配置文件類Config.java,在這個類里面定義一些與版本相關(guān)的常量

package com.test.update.config; 
 
public class Config { 
 //版本信息 
 public static int localVersion = 0; 
 public static int serverVersion = 0; 
 /* 下載包安裝路徑 */ 
 public static final String savePath = "/sdcard/test/"; 
 
 public static final String saveFileName = savePath + "test.apk"; 
}

4.編寫更新服務(wù)類UpdateServcie.java

package com.test.update; 
 
import java.io.File; 
import java.io.FileOutputStream; 
import java.io.InputStream; 
import java.net.HttpURLConnection; 
import java.net.URL; 
 
import android.annotation.SuppressLint; 
import android.app.Notification; 
import android.app.NotificationManager; 
import android.app.PendingIntent; 
import android.app.Service; 
import android.content.Intent; 
import android.net.Uri; 
import android.os.Environment; 
import android.os.Handler; 
import android.os.IBinder; 
import android.os.Message; 
import android.widget.RemoteViews; 
 
import com.test.update.config.Config; 
 
public class UpdateService extends Service { 
 // 標題 
 private int titleId = 0; 
 
 // 文件存儲 
 private File updateDir = null; 
 private File updateFile = null; 
 // 下載狀態(tài) 
 private final static int DOWNLOAD_COMPLETE = 0; 
 private final static int DOWNLOAD_FAIL = 1; 
 // 通知欄 
 private NotificationManager updateNotificationManager = null; 
 private Notification updateNotification = null; 
 // 通知欄跳轉(zhuǎn)Intent 
 private Intent updateIntent = null; 
 private PendingIntent updatePendingIntent = null; 
 /*** 
  * 創(chuàng)建通知欄 
  */ 
 RemoteViews contentView; 
 // 這樣的下載代碼很多,我就不做過多的說明 
 int downloadCount = 0; 
 int currentSize = 0; 
 long totalSize = 0; 
 int updateTotalSize = 0; 
 
 // 在onStartCommand()方法中準備相關(guān)的下載工作: 
 @SuppressWarnings("deprecation") 
 @Override 
 public int onStartCommand(Intent intent, int flags, int startId) { 
  // 獲取傳值 
  titleId = intent.getIntExtra("titleId", 0); 
  // 創(chuàng)建文件 
  if (android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment 
    .getExternalStorageState())) { 
   updateDir = new File(Environment.getExternalStorageDirectory(), 
     Config.saveFileName); 
   updateFile = new File(updateDir.getPath(), getResources() 
     .getString(titleId) + ".apk"); 
  } 
 
  this.updateNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 
  this.updateNotification = new Notification(); 
 
  // 設(shè)置下載過程中,點擊通知欄,回到主界面 
  updateIntent = new Intent(this, UpdateActivity.class); 
  updatePendingIntent = PendingIntent.getActivity(this, 0, updateIntent, 
    0); 
  // 設(shè)置通知欄顯示內(nèi)容 
  updateNotification.icon = R.drawable.ic_launcher; 
  updateNotification.tickerText = "開始下載"; 
  updateNotification.setLatestEventInfo(this, "QQ", "0%", 
    updatePendingIntent); 
  // 發(fā)出通知 
  updateNotificationManager.notify(0, updateNotification); 
 
  // 開啟一個新的線程下載,如果使用Service同步下載,會導致ANR問題,Service本身也會阻塞 
  new Thread(new updateRunnable()).start();// 這個是下載的重點,是下載的過程 
 
  return super.onStartCommand(intent, flags, startId); 
 } 
 
 @Override 
 public IBinder onBind(Intent arg0) { 
  // TODO Auto-generated method stub 
  return null; 
 } 
 
 @SuppressLint("HandlerLeak") 
 private Handler updateHandler = new Handler() { 
  @Override 
  public void handleMessage(Message msg) { 
   switch (msg.what) { 
     
   case DOWNLOAD_COMPLETE: 
    // 點擊安裝PendingIntent 
    Uri uri = Uri.fromFile(updateFile); 
    Intent installIntent = new Intent(Intent.ACTION_VIEW); 
    installIntent.setDataAndType(uri, 
      "application/vnd.android.package-archive"); 
 
    updatePendingIntent = PendingIntent.getActivity( 
      UpdateService.this, 0, installIntent, 0); 
 
    updateNotification.defaults = Notification.DEFAULT_SOUND;// 鈴聲提醒 
    updateNotification.setLatestEventInfo(UpdateService.this, 
      "QQ", "下載完成,點擊安裝。", updatePendingIntent); 
    updateNotificationManager.notify(0, updateNotification); 
 
    // 停止服務(wù) 
    stopService(updateIntent); 
   case DOWNLOAD_FAIL: 
    // 下載失敗 
    updateNotification.setLatestEventInfo(UpdateService.this, 
      "QQ", "下載完成,點擊安裝。", updatePendingIntent); 
    updateNotificationManager.notify(0, updateNotification); 
   default: 
    stopService(updateIntent); 
   } 
  } 
 }; 
 
 public long downloadUpdateFile(String downloadUrl, File saveFile) 
   throws Exception { 
 
  HttpURLConnection httpConnection = null; 
  InputStream is = null; 
  FileOutputStream fos = null; 
 
  try { 
   URL url = new URL(downloadUrl); 
   httpConnection = (HttpURLConnection) url.openConnection(); 
   httpConnection 
     .setRequestProperty("User-Agent", "PacificHttpClient"); 
   if (currentSize > 0) { 
    httpConnection.setRequestProperty("RANGE", "bytes=" 
      + currentSize + "-"); 
   } 
   httpConnection.setConnectTimeout(10000); 
   httpConnection.setReadTimeout(20000); 
   updateTotalSize = httpConnection.getContentLength(); 
   if (httpConnection.getResponseCode() == 404) { 
    throw new Exception("fail!"); 
   } 
   is = httpConnection.getInputStream(); 
   fos = new FileOutputStream(saveFile, false); 
   byte buffer[] = new byte[4096]; 
   int readsize = 0; 
   while ((readsize = is.read(buffer)) > 0) { 
    fos.write(buffer, 0, readsize); 
    totalSize += readsize; 
    // 為了防止頻繁的通知導致應(yīng)用吃緊,百分比增加10才通知一次 
    if ((downloadCount == 0) 
      || (int) (totalSize * 100 / updateTotalSize) - 10 > downloadCount) { 
     downloadCount += 10; 
 
     updateNotification.setLatestEventInfo(UpdateService.this, 
       "正在下載", (int) totalSize * 100 / updateTotalSize 
         + "%", updatePendingIntent); 
 
      
     /*** 
      * 在這里我們用自定的view來顯示Notification 
      */ 
     updateNotification.contentView = new RemoteViews( 
       getPackageName(), R.layout.notification_item); 
     updateNotification.contentView.setTextViewText( 
       R.id.notificationTitle, "正在下載"); 
     updateNotification.contentView.setProgressBar( 
       R.id.notificationProgress, 100, downloadCount, false); 
      
     updateNotificationManager.notify(0, updateNotification); 
    } 
   } 
  } finally { 
   if (httpConnection != null) { 
    httpConnection.disconnect(); 
   } 
   if (is != null) { 
    is.close(); 
   } 
   if (fos != null) { 
    fos.close(); 
   } 
  } 
  return totalSize; 
 } 
 
 class updateRunnable implements Runnable { 
  Message message = updateHandler.obtainMessage(); 
 
  public void run() { 
   message.what = DOWNLOAD_COMPLETE; 
    
    
   try { 
    // 增加權(quán)限<USES-PERMISSION 
    // android:name="android.permission.WRITE_EXTERNAL_STORAGE">; 
    if (!updateDir.exists()) { 
     updateDir.mkdirs(); 
    } 
    if (!updateFile.exists()) { 
     updateFile.createNewFile(); 
    } 
    // 下載函數(shù),以QQ為例子 
    // 增加權(quán)限<USES-PERMISSION 
    // android:name="android.permission.INTERNET">; 
    long downloadSize = downloadUpdateFile( 
      "http://softfile.3g.qq.com:8080/msoft/179/1105/10753/MobileQQ1.0(Android)_Build0198.apk", 
      updateFile); 
    if (downloadSize > 0) { 
     // 下載成功 
     updateHandler.sendMessage(message); 
    } 
   } catch (Exception ex) { 
    ex.printStackTrace(); 
    message.what = DOWNLOAD_FAIL; 
    // 下載失敗 
    updateHandler.sendMessage(message); 
   } 
  } 
 } 
}

5.編寫活動類UpdateActivity

package com.test.update; 
 
import com.test.update.config.Config; 
 
import android.support.v4.app.Fragment; 
import android.app.Activity; 
import android.app.AlertDialog; 
import android.content.DialogInterface; 
import android.content.Intent; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.LayoutInflater; 
 
public class UpdateActivity extends Activity { 
 
 @Override 
 protected void onCreate(Bundle savedInstanceState) { 
  super.onCreate(savedInstanceState); 
  setContentView(R.layout.activity_main); 
  checkVersion(); 
 } 
 
 /** 
  * 檢查更新版本 
  */ 
 public void checkVersion() { 
 
  if (Config.localVersion < Config.serverVersion) { 
   Log.i("hgncxzy", "=============================="); 
   // 發(fā)現(xiàn)新版本,提示用戶更新 
   AlertDialog.Builder alert = new AlertDialog.Builder(this); 
   alert.setTitle("軟件升級") 
     .setMessage("發(fā)現(xiàn)新版本,建議立即更新使用.") 
     .setPositiveButton("更新", 
       new DialogInterface.OnClickListener() { 
        public void onClick(DialogInterface dialog, 
          int which) { 
         // 開啟更新服務(wù)UpdateService 
         // 這里為了把update更好模塊化,可以傳一些updateService依賴的值 
         // 如布局ID,資源ID,動態(tài)獲取的標題,這里以app_name為例 
         Intent updateIntent = new Intent( 
           UpdateActivity.this, 
           UpdateService.class); 
         updateIntent.putExtra("titleId", 
           R.string.app_name); 
         startService(updateIntent); 
        } 
       }) 
     .setNegativeButton("取消", 
       new DialogInterface.OnClickListener() { 
        public void onClick(DialogInterface dialog, 
          int which) { 
         dialog.dismiss(); 
        } 
       }); 
   alert.create().show(); 
  } else { 
   // 清理工作,略去 
   // cheanUpdateFile() 
  } 
 } 
}

6.添加權(quán)限以及將服務(wù)靜態(tài)加載(在配置文件中加載)。

<uses-permission android:name="android.permission.INTERNET" /> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

注冊服務(wù)

<service android:name="com.test.update.UpdateService" > 
  </service>

完整的AndroidManifest.xml文件如下:

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
 package="com.test.update" 
 android:versionCode="1" 
 android:versionName="1.0" > 
 
 <uses-sdk 
  android:minSdkVersion="8" 
  android:targetSdkVersion="8" /> 
 
 <application 
  android:name="com.test.application.AppContext" 
  android:icon="@drawable/ic_launcher" 
  android:label="@string/app_name" > 
  <activity 
   android:name="com.test.update.UpdateActivity" 
   android:label="@string/app_name" > 
   <intent-filter> 
    <action android:name="android.intent.action.MAIN" /> 
 
    <category android:name="android.intent.category.LAUNCHER" /> 
   </intent-filter> 
  </activity> 
  <service android:name="com.test.update.UpdateService" > 
  </service> 
 </application> 
 
 <uses-permission android:name="android.permission.INTERNET" /> 
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> 
 
</manifest>

關(guān)于“Android實現(xiàn)APP自動更新功能的方法”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI