Android:以编程方式安装。apk

我在帮助下做了这个 Android下载二进制文件问题 和在Android上以编程方式安装应用程序。< / p >

我想同时进行自动更新和自动安装。它是本地的,所以是非市场应用。

下面是我的代码:

public void Update(String apkurl){
try {
URL url = new URL(apkurl);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();


String PATH = Environment.getExternalStorageDirectory() + "/download/";
File file = new File(PATH);
file.mkdirs();
File outputFile = new File(file, "app.apk");
FileOutputStream fos = new FileOutputStream(outputFile);


InputStream is = c.getInputStream();


byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.close();
is.close();//till here, it works fine - .apk is download to my sdcard in download file


Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse(PATH+"app.apk"))
.setType("application/android.com.app");
startActivity(promptInstall);//installation is not working


} catch (IOException e) {
Toast.makeText(getApplicationContext(), "Update error!", Toast.LENGTH_LONG).show();
}
}

我的权限是INTERNETWRITE_EXTERNAL_STORAGEINSTALL_PACKAGESDELETE_PACKAGES

当Intent promptInstall加载时,应用程序崩溃=/

那么,我是否缺少权限或我的代码不正确,或者是否有更好的方法来做到这一点?

418264 次浏览

我解决了这个问题。我在setData(Uri)setType(String)中犯了错误。

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/download/" + "app.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

现在是正确的,我的自动更新正在工作。谢谢你的帮助。=)

编辑20.7.2016:

过了很长一段时间,我不得不在另一个项目中再次使用这种更新方式。我用旧的解决方案遇到了一些问题。在这段时间里,很多事情都发生了变化,所以我必须用不同的方法来做这件事。代码如下:

    //get destination to update file and set Uri
//TODO: First I wanted to store my update .apk file on internal storage for my app but apparently android does not allow you to open and install
//aplication with existing package from there. So for me, alternative solution is Download directory in external storage. If there is better
//solution, please inform us in comment
String destination = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/";
String fileName = "AppName.apk";
destination += fileName;
final Uri uri = Uri.parse("file://" + destination);


//Delete update file if exists
File file = new File(destination);
if (file.exists())
//file.delete() - test this, I think sometimes it doesnt work
file.delete();


//get url of app on server
String url = Main.this.getString(R.string.update_app_url);


//set downloadmanager
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription(Main.this.getString(R.string.notification_description));
request.setTitle(Main.this.getString(R.string.app_name));


//set destination
request.setDestinationUri(uri);


// get download service and enqueue file
final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
final long downloadId = manager.enqueue(request);


//set BroadcastReceiver to install app when .apk is downloaded
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
install.setDataAndType(uri,
manager.getMimeTypeForDownloadedFile(downloadId));
startActivity(install);


unregisterReceiver(this);
finish();
}
};
//register receiver for when .apk download is compete
registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
/*
*  Code Prepared by **Muhammad Mubashir**.
*  Analyst Software Engineer.
Email Id : muhammad.mubashir.bscs@gmail.com
Skype Id : muhammad.mubashir.ansari
Code: **August, 2011.**


Description: **Get Updates(means New .Apk File) from IIS Server and Download it on Device SD Card,
and Uninstall Previous (means OLD .apk) and Install New One.
and also get Installed App Version Code & Version Name.**


All Rights Reserved.
*/
package com.SelfInstall01;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import com.SelfInstall01.SelfInstall01Activity;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;


public class SelfInstall01Activity extends Activity
{
class PInfo {
private String appname = "";
private String pname = "";
private String versionName = "";
private int versionCode = 0;
//private Drawable icon;
/*private void prettyPrint() {
//Log.v(appname + "\t" + pname + "\t" + versionName + "\t" + versionCode);
}*/
}
public int VersionCode;
public String VersionName="";
public String ApkName ;
public String AppName ;
public String BuildVersionPath="";
public String urlpath ;
public String PackageName;
public String InstallAppPackageName;
public String Text="";


TextView tvApkStatus;
Button btnCheckUpdates;
TextView tvInstallVersion;


/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);


//Text= "Old".toString();
Text= "New".toString();




ApkName = "SelfInstall01.apk";//"Test1.apk";// //"DownLoadOnSDcard_01.apk"; //
AppName = "SelfInstall01";//"Test1"; //


BuildVersionPath = "http://10.0.2.2:82/Version.txt".toString();
PackageName = "package:com.SelfInstall01".toString(); //"package:com.Test1".toString();
urlpath = "http://10.0.2.2:82/"+ Text.toString()+"_Apk/" + ApkName.toString();


tvApkStatus =(TextView)findViewById(R.id.tvApkStatus);
tvApkStatus.setText(Text+" Apk Download.".toString());




tvInstallVersion = (TextView)findViewById(R.id.tvInstallVersion);
String temp = getInstallPackageVersionInfo(AppName.toString());
tvInstallVersion.setText("" +temp.toString());


btnCheckUpdates =(Button)findViewById(R.id.btnCheckUpdates);
btnCheckUpdates.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
GetVersionFromServer(BuildVersionPath);


if(checkInstalledApp(AppName.toString()) == true)
{
Toast.makeText(getApplicationContext(), "Application Found " + AppName.toString(), Toast.LENGTH_SHORT).show();




}else{
Toast.makeText(getApplicationContext(), "Application Not Found. "+ AppName.toString(), Toast.LENGTH_SHORT).show();
}
}
});


}// On Create END.


private Boolean checkInstalledApp(String appName){
return getPackages(appName);
}


// Get Information about Only Specific application which is Install on Device.
public String getInstallPackageVersionInfo(String appName)
{
String InstallVersion = "";
ArrayList<PInfo> apps = getInstalledApps(false); /* false = no system packages */
final int max = apps.size();
for (int i=0; i<max; i++)
{
//apps.get(i).prettyPrint();
if(apps.get(i).appname.toString().equals(appName.toString()))
{
InstallVersion = "Install Version Code: "+ apps.get(i).versionCode+
" Version Name: "+ apps.get(i).versionName.toString();
break;
}
}


return InstallVersion.toString();
}
private Boolean getPackages(String appName)
{
Boolean isInstalled = false;
ArrayList<PInfo> apps = getInstalledApps(false); /* false = no system packages */
final int max = apps.size();
for (int i=0; i<max; i++)
{
//apps.get(i).prettyPrint();


if(apps.get(i).appname.toString().equals(appName.toString()))
{
/*if(apps.get(i).versionName.toString().contains(VersionName.toString()) == true &&
VersionCode == apps.get(i).versionCode)
{
isInstalled = true;
Toast.makeText(getApplicationContext(),
"Code Match", Toast.LENGTH_SHORT).show();
openMyDialog();
}*/
if(VersionCode <= apps.get(i).versionCode)
{
isInstalled = true;


/*Toast.makeText(getApplicationContext(),
"Install Code is Less.!", Toast.LENGTH_SHORT).show();*/


DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which)
{
case DialogInterface.BUTTON_POSITIVE:
//Yes button clicked
//SelfInstall01Activity.this.finish(); Close The App.


DownloadOnSDcard();
InstallApplication();
UnInstallApplication(PackageName.toString());


break;


case DialogInterface.BUTTON_NEGATIVE:
//No button clicked


break;
}
}
};


AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("New Apk Available..").setPositiveButton("Yes Proceed", dialogClickListener)
.setNegativeButton("No.", dialogClickListener).show();


}
if(VersionCode > apps.get(i).versionCode)
{
isInstalled = true;
/*Toast.makeText(getApplicationContext(),
"Install Code is better.!", Toast.LENGTH_SHORT).show();*/


DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which)
{
case DialogInterface.BUTTON_POSITIVE:
//Yes button clicked
//SelfInstall01Activity.this.finish(); Close The App.


DownloadOnSDcard();
InstallApplication();
UnInstallApplication(PackageName.toString());


break;


case DialogInterface.BUTTON_NEGATIVE:
//No button clicked


break;
}
}
};


AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("NO need to Install.").setPositiveButton("Install Forcely", dialogClickListener)
.setNegativeButton("Cancel.", dialogClickListener).show();
}
}
}


return isInstalled;
}
private ArrayList<PInfo> getInstalledApps(boolean getSysPackages)
{
ArrayList<PInfo> res = new ArrayList<PInfo>();
List<PackageInfo> packs = getPackageManager().getInstalledPackages(0);


for(int i=0;i<packs.size();i++)
{
PackageInfo p = packs.get(i);
if ((!getSysPackages) && (p.versionName == null)) {
continue ;
}
PInfo newInfo = new PInfo();
newInfo.appname = p.applicationInfo.loadLabel(getPackageManager()).toString();
newInfo.pname = p.packageName;
newInfo.versionName = p.versionName;
newInfo.versionCode = p.versionCode;
//newInfo.icon = p.applicationInfo.loadIcon(getPackageManager());
res.add(newInfo);
}
return res;
}




public void UnInstallApplication(String packageName)// Specific package Name Uninstall.
{
//Uri packageURI = Uri.parse("package:com.CheckInstallApp");
Uri packageURI = Uri.parse(packageName.toString());
Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
startActivity(uninstallIntent);
}
public void InstallApplication()
{
Uri packageURI = Uri.parse(PackageName.toString());
Intent intent = new Intent(android.content.Intent.ACTION_VIEW, packageURI);


//      Intent intent = new Intent(android.content.Intent.ACTION_VIEW);


//intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent.setFlags(Intent.ACTION_PACKAGE_REPLACED);


//intent.setAction(Settings. ACTION_APPLICATION_SETTINGS);


intent.setDataAndType
(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/download/"  + ApkName.toString())),
"application/vnd.android.package-archive");


// Not open this Below Line Because...
////intent.setClass(this, Project02Activity.class); // This Line Call Activity Recursively its dangerous.


startActivity(intent);
}
public void GetVersionFromServer(String BuildVersionPath)
{
//this is the file you want to download from the remote server
//path ="http://10.0.2.2:82/Version.txt";
//this is the name of the local file you will create
// version.txt contain Version Code = 2; \n Version name = 2.1;
URL u;
try {
u = new URL(BuildVersionPath.toString());


HttpURLConnection c = (HttpURLConnection) u.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();


//Toast.makeText(getApplicationContext(), "HttpURLConnection Complete.!", Toast.LENGTH_SHORT).show();


InputStream in = c.getInputStream();


ByteArrayOutputStream baos = new ByteArrayOutputStream();


byte[] buffer = new byte[1024]; //that stops the reading after 1024 chars..
//in.read(buffer); //  Read from Buffer.
//baos.write(buffer); // Write Into Buffer.


int len1 = 0;
while ( (len1 = in.read(buffer)) != -1 )
{
baos.write(buffer,0, len1); // Write Into ByteArrayOutputStream Buffer.
}


String temp = "";
String s = baos.toString();// baos.toString(); contain Version Code = 2; \n Version name = 2.1;


for (int i = 0; i < s.length(); i++)
{
i = s.indexOf("=") + 1;
while (s.charAt(i) == ' ') // Skip Spaces
{
i++; // Move to Next.
}
while (s.charAt(i) != ';'&& (s.charAt(i) >= '0' && s.charAt(i) <= '9' || s.charAt(i) == '.'))
{
temp = temp.toString().concat(Character.toString(s.charAt(i))) ;
i++;
}
//
s = s.substring(i); // Move to Next to Process.!
temp = temp + " "; // Separate w.r.t Space Version Code and Version Name.
}
String[] fields = temp.split(" ");// Make Array for Version Code and Version Name.


VersionCode = Integer.parseInt(fields[0].toString());// .ToString() Return String Value.
VersionName = fields[1].toString();


baos.close();
}
catch (MalformedURLException e) {
Toast.makeText(getApplicationContext(), "Error." + e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Error." + e.getMessage(), Toast.LENGTH_SHORT).show();
}
//return true;
}// Method End.


// Download On My Mobile SDCard or Emulator.
public void DownloadOnSDcard()
{
try{
URL url = new URL(urlpath.toString()); // Your given URL.


HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect(); // Connection Complete here.!


//Toast.makeText(getApplicationContext(), "HttpURLConnection complete.", Toast.LENGTH_SHORT).show();


String PATH = Environment.getExternalStorageDirectory() + "/download/";
File file = new File(PATH); // PATH = /mnt/sdcard/download/
if (!file.exists()) {
file.mkdirs();
}
File outputFile = new File(file, ApkName.toString());
FileOutputStream fos = new FileOutputStream(outputFile);


//      Toast.makeText(getApplicationContext(), "SD Card Path: " + outputFile.toString(), Toast.LENGTH_SHORT).show();


InputStream is = c.getInputStream(); // Get from Server and Catch In Input Stream Object.


byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1); // Write In FileOutputStream.
}
fos.close();
is.close();//till here, it works fine - .apk is download to my sdcard in download file.
// So please Check in DDMS tab and Select your Emulator.


//Toast.makeText(getApplicationContext(), "Download Complete on SD Card.!", Toast.LENGTH_SHORT).show();
//download the APK to sdcard then fire the Intent.
}
catch (IOException e)
{
Toast.makeText(getApplicationContext(), "Error! " +
e.toString(), Toast.LENGTH_LONG).show();
}
}
}

对于ICS,我已经实现了您的代码,并创建了一个扩展AsyncTask的类。希望你能欣赏!感谢您的代码和解决方案。

public class UpdateApp extends AsyncTask<String,Void,Void>{
private Context context;
public void setContext(Context contextf){
context = contextf;
}


@Override
protected Void doInBackground(String... arg0) {
try {
URL url = new URL(arg0[0]);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();


String PATH = "/mnt/sdcard/Download/";
File file = new File(PATH);
file.mkdirs();
File outputFile = new File(file, "update.apk");
if(outputFile.exists()){
outputFile.delete();
}
FileOutputStream fos = new FileOutputStream(outputFile);


InputStream is = c.getInputStream();


byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.close();
is.close();


Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File("/mnt/sdcard/Download/update.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error!
context.startActivity(intent);




} catch (Exception e) {
Log.e("UpdateAPP", "Update error! " + e.getMessage());
}
return null;
}
}

要使用它,在你的主活动中以这样的方式调用:

atualizaApp = new UpdateApp();
atualizaApp.setContext(getApplicationContext());
atualizaApp.execute("http://serverurl/appfile.apk");

谢谢你的分享。我已经实现并工作了。然而:

1)我安装了我的应用程序(工作没有问题)
2)我在服务器上放置超过2个。应用程序检索ver2并保存到SD卡,并提示用户安装新的包ver2
3) ver2安装并正常工作
4)问题是,每次应用启动时,它都希望用户重新安装版本2

所以我认为解决方案是简单地删除sdcard上的APK,但异步任务将简单地再次为服务器检索ver2。

因此,阻止再次尝试安装v2 apk的唯一方法是从sdcard和远程服务器中删除。

正如您可以想象的那样,这是行不通的,因为我永远不知道所有用户何时收到了最新版本。

任何帮助解决这个问题都是非常感激的。

我实现了上面列出的“ldmuniz”方法。

我只是在想所有APK的名字都是一样的。我是否应该命名myapk_v1.0 . xx.apk,并在该版本中主动设置远程路径,以便在发布时查找v.2.0 ?

我测试了这个理论,它确实解决了问题。 你需要为你的APK文件命名一些版本,记住总是在你当前发布的应用中设置你的NEXT发布版本#