下面文章转载自:http://blog.csdn.net/MyDreamIsNotNull/article/details/53896034
其中有几个我遇到的问题记录一下:
1、新建安卓工程:File->New->Android Application Project
2、把下面的代码放入工程
3、编译发包
4、发现unity调用后不能正常下载安装,把AsyncTaskRunnable的两句代码注释掉,才可以正常下载安装,具体原因暂未找到。
mNotification.icon = R.drawable.ic_launcher;//设置下载进度的icon
mNotification.tickerText = mContext.getResources().getString(R.string.app_name); //设置下载进度的title
最近在学习android的知识,因为功能的需要,unity3d 要和android交互。最近公司的项目中遇到了unity3d 开发的app需要更新和自动安装的功能,但是使用unity3d是实现不了这样的功能的。这个时候就想到使用android原有的知识进行开发,然后打成jar包,导成插件,放到unity3d中去调用。
1.首先在eclipse或者android studio 中开发原有的app下载更新的功能。
*我这里是以eclipse为例开发的。
首先在eclipse中导入unity3d的jar包,class.jar,这里的class.jar在unity3d安装目录下,如下:D:\Unity5.2.3SetUp\Editor\Data\PlaybackEngines\androidplayer\Variations\il2cpp\Development\Classes
然后就要写相应的代码了,android的原有下载更新网上也有很多但是没有结合Untiy3d。具体的代码如下,首先要引入import com.unity3d.player.UnityPlayer,然后activity继承UnityPlayerActivity
package com.example.downapk;
import java.io.File;
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends UnityPlayerActivity
{
private static String tag = "MainActivity";
private Handler mHandler = new Handler()
{
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
OpenFile();
break;
default:
break;
}
}
};
private AsyncTaskUtil mDownloadAsyncTask;
public float size;
public static MainActivity instance;
public static MainActivity getInstance()
{
return instance;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
}
public void SetSize(float size)
{
size = this.size;
Log.d(tag, this.size+"");
}
public void StartLoad(String httpUrl, String apkName)
{
mDownloadAsyncTask = new AsyncTaskUtil(MainActivity.this, mHandler);
mDownloadAsyncTask.execute(httpUrl, apkName);//必须传入两个参数——参数1:url;参数2:文件名(可以为null)
}
private void OpenFile() {
String str = "/test.apk";
String fileName = mDownloadAsyncTask.getFolderPath() + str;
File file = new File(fileName);
if (!file.exists()) {
Log.d("URL", "fileName:" + fileName);
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
Log.d("URL", "fileName:" + fileName);
startActivity(intent);
}
}
上面代码是主activity 表黑部分是要在unity中调用的。调用可以是一个按钮,例如
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
jo.Call("StartLoad", ParentApp.getInstance().updateUrl, "test.apk");
这里是使用AsyncTask进行下载的,在后来可以看到下载进度。下面下载的代码。
package com.example.downapk;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.text.DecimalFormat;
import java.util.Timer;
import java.util.TimerTask;
import com.unity3d.player.UnityPlayer;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
public class AsyncTaskUtil extends AsyncTask<String, Double, Boolean>
{
public static final String TAG = "AsyncTaskUtil";
public static final int NOTIFICATION_PROGRESS_UPDATE = 0x10;//用于更新下载进度的标志
public static final int NOTIFICATION_PROGRESS_SUCCEED = 0x11;//表示下载成功
public static final int NOTIFICATION_PROGRESS_FAILED = 0x12;//表示下载失败
//URL
private String mUrl;
//activity
private Context mContext;
//任务定时器
private Timer mTimer;
//定时任务
private TimerTask mTask;
//主线程传递过来的handler
private Handler mHandler;
//所要下载的文件大小
private long mFileSize;
//已下载的文件大小
private long mTotalReadSize;
//AsyncTaskRunnable实现了Runnable接口,用于更新下载进度的显示
private AsyncTaskRunnable mRunnable;
//构造方法
public AsyncTaskUtil(Context context, Handler handler) {
mContext = context;
mHandler = handler;
mTimer = new Timer();
mTask = new TimerTask() {//在run方法中执行定时的任务
@Override
public void run() {
//size表示下载进度的百分比
float size = (float) mTotalReadSize * 100 / (float) mFileSize;
//通过AsyncTaskRunnable的setDatas方法下载的进度和状态(更新中、失败、成功)
mRunnable.setDatas(NOTIFICATION_PROGRESS_UPDATE, size);
//更新进度
mHandler.post(mRunnable);
}
};
mRunnable = new AsyncTaskRunnable(mContext);
}
// 执行耗时操作,params[0]为url,params[1]为文件名(空则写入null)
@Override
protected Boolean doInBackground(String... params) {
//任务定时器一定要启动
mTimer.schedule(mTask, 0, 500);
try {
mUrl = params[0];
//建立链接
URLConnection connection = new URL(mUrl).openConnection();
//获取文件大小
mFileSize = connection.getContentLength();
Log.d(TAG, "the count of the url content length is : " + mFileSize);
//获得输入流
InputStream is = connection.getInputStream();
//先建立文件夹
File fold = new File(getFolderPath());
if (!fold.exists()) {
fold.mkdirs();
}
String fileName = "";
//判断文件名:用户自定义或由url获得
if(params[1] != null){
fileName = params[1];
} else{
fileName = getFileName(params[0]);
}
//文件输出流
FileOutputStream fos = new FileOutputStream(new File(getFolderPath()
+ fileName));
byte[] buff = new byte[1024];
int len;
while ((len = is.read(buff)) != -1) {
mTotalReadSize += len;
MainActivity.getInstance().size = (float) mTotalReadSize * 100 / (float) mFileSize;
DecimalFormat format = new DecimalFormat("0");
UnityPlayer.UnitySendMessage("UI Root","GetSize",format.format(MainActivity.getInstance().size)+"");
Log.d(TAG, "the count of the url content length is : " + MainActivity.getInstance().size);
fos.write(buff, 0, len);
}
fos.flush();
fos.close();
} catch (Exception e) {
//异常,下载失败
mRunnable.setDatas(NOTIFICATION_PROGRESS_FAILED, 0);
//发送显示下载失败
mHandler.post(mRunnable);
if(mTimer != null && mTask != null){
mTimer.cancel();
mTask.cancel();
}
e.printStackTrace();
return false;
}
//下载成功
mRunnable.setDatas(NOTIFICATION_PROGRESS_SUCCEED, 0);
mHandler.post(mRunnable);
if(mTimer != null && mTask != null){
mTimer.cancel();
mTask.cancel();
}
return true;
}
//由url获得文件名
private String getFileName(String string) {
return string.substring(string.lastIndexOf("/") + 1);
}
//下载文件夹路径
public String getFolderPath() {
return Environment.getExternalStorageDirectory().toString() + "/AsyncTaskDownload/";
}
// doInBackground方法之前调用,初始化UI
@Override
protected void onPreExecute() {
super.onPreExecute();
}
// 在doInBackground方法之后调用
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
Toast.makeText(mContext, "Download Completed ! ", Toast.LENGTH_SHORT).show();
mHandler.sendEmptyMessage(1);
} else {
Toast.makeText(mContext, "Download Failed ! ", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onProgressUpdate(Double... values) {
super.onProgressUpdate(values);
}
}
标红的地方是王unity中传的,我想要在unity中看到下载的进度,当时试了unity调用android都没有成功,后来想从android中往unity中传
UnityPlayer.UnitySendMessage("UI Root","GetSize",format.format(MainActivity.getInstance().size)+"");
UI Root 表示在untiy中有这样一个gameobject 在gameobject 上面挂着一个脚本,脚本中有一个方法名称为GetSize(),最后一个为要在unity中看到的进度。
在unity中的调用代码为
public void GetSize(string str)
{
mSize = str;
Debug.Log(mSize + "************************");
text.text = "下载进度: " + mSize + "%";
if (mSize == "100")
{
Application.Quit();
}
}
这个方法不需要在别的地方调用的。
二:在后台显示下载进度,代码如下:
package com.example.downapk;
import java.text.DecimalFormat;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;
public class AsyncTaskRunnable implements Runnable{
public static final String TAG = "AsyncTaskRunnable";
//主线程的activity
private Context mContext;
//notification的状态:更新 or 失败 or 成功
private int mStatus;
//notification的下载比例
private float mSize;
//管理下拉菜单的通知信息
private NotificationManager mNotificationManager;
//下拉菜单的通知信息
private Notification mNotification;
//下拉菜单的通知信息的view
private RemoteViews mRemoteViews;
//下拉菜单的通知信息的种类id
private static final int NOTIFICATION_ID = 1;
//设置比例和数据
public void setDatas(int status , float size) {
this.mStatus = status;
this.mSize = size;
}
//初始化
public AsyncTaskRunnable(Context context) {
this.mContext = context;
mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
//初始化下拉菜单的通知信息
mNotification = new Notification();
mNotification.icon = R.drawable.ic_launcher;//设置下载进度的icon
mNotification.tickerText = mContext.getResources().getString(R.string.app_name); //设置下载进度的title
mRemoteViews = new RemoteViews(mContext.getPackageName(), R.layout.down_notification);//对于RemoteView的使用,不懂的需要查找google
//mRemoteViews.setImageViewResource(R.id.id_download_icon, R.drawable.ic_launcher);
}
@Override
public void run() {//通过判断不同的状态:更新中/下载失败/下载成功 更新下拉菜单的通知信息
switch (mStatus) {
case AsyncTaskUtil.NOTIFICATION_PROGRESS_FAILED://下载失败
mNotificationManager.cancel(NOTIFICATION_ID);
break;
case AsyncTaskUtil.NOTIFICATION_PROGRESS_SUCCEED://下载成功
mRemoteViews.setTextViewText(R.id.id_download_textview, "Download completed ! ");
mRemoteViews.setProgressBar(R.id.id_download_progressbar, 100, 100, false);
mNotification.contentView = mRemoteViews;
mNotificationManager.notify(NOTIFICATION_ID, mNotification);
mNotificationManager.cancel(NOTIFICATION_ID);
Toast.makeText(mContext, "Download completed ! ", Toast.LENGTH_SHORT).show();
break;
case AsyncTaskUtil.NOTIFICATION_PROGRESS_UPDATE://更新中
DecimalFormat format = new DecimalFormat("0.00");//数字格式转换
String progress = format.format(mSize);
Log.d(TAG, "the progress of the download " + progress);
mRemoteViews.setTextViewText(R.id.id_download_textview, "Download completed : " + progress + " %");
mRemoteViews.setProgressBar(R.id.id_download_progressbar, 100, (int)mSize, false);
mNotification.contentView = mRemoteViews;
mNotificationManager.notify(NOTIFICATION_ID, mNotification);
break;
}
}
}
创建一个activity代码,down_notification.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:background="@android:color/white" >
<ImageView
android:id="@+id/id_download_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="3dp"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:layout_marginLeft="3dp"
android:src="@drawable/ic_launcher" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dip"
android:layout_weight="1"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="@android:color/black"
android:textSize="18dip"
android:textStyle="bold" />
<ProgressBar
android:id="@+id/id_download_progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:max="100"
android:progress="0"
android:secondaryProgress="0" />
<TextView
android:id="@+id/id_download_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Download completed : "
android:textColor="@android:color/black"
android:textSize="12dip"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
这些都完成后要在mianfest.xml中配置相应的权限了,首先是联网的权限,然后是读写sdk的权限
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
重点在于:这些都完成后导出jar包,我这里是直接在eclipse中导出的也可以用别的方法,百度得知。导jar包的步骤如下:右键项目->Export->java->jar file->next->
在这里只选中src 文件,其余的全部勾掉,否则打出jar包有问题。
jar包导出后放到untiy中的Plugins中的android中的bin文件夹下,然后再将res拷到android下。接下来要将AndroidMiandfest.xml放到android文件夹下。如果以前项目中已经继承了别的插件后,这里就要注意了。首先将我们导入的配置文件删掉,配置原来的既可以,配置的时候要注意,要将原来的 <intent-filter>
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN" />
</intent-filter>
注释掉,将换成新导入的如下即可。此时所有的工作都完成了。
<activity
android:name="com.example.downapk.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>