例如我要在浏览器中打开一个网址,我们就可以这样写一个Intent。当手机上的应用有声明这个Action为android.intent.action.VIEW并且data的声明的scheme为http的活动时,就会被相应的激活。
Button button5 = (Button)findViewById(R.id.button5);
button5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW);//android.intent.action.VIEW
intent.setData(Uri.parse("http://www.chicai.group"));//设置一个uri
startActivity(intent);
}
});intent.setData对应的声明就是data标签写的属性 :
android:scheme用于指定数据的协议部分,如http
android:host用于指定数据的主机名部分,如www.chicai.group
android:port用于指定数据的端口部分
android:path用于指定主机名和端口之后的部分
android:mimeType用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
<intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="http" android:host="主机名" android:path="/open"/> </intent-filter>
再例如打电话:
Button button6 = (Button)findViewById(R.id.button6);
button6.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
});
Intent是Android程序中各组件之间进行交互的一种重要方式,不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动、启动服务以及发送光比等场景。可以通过Intent从一个活动跳转到另外一个活动,实现活动和活动之间的交换。
一、显式Intent
Button button3 = (Button) findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//第一个参数为启动活动的上下文,第二个为需要启动的目标活动
Intent intent = new Intent(Main2Activity.this,Main23Activity.class);
startActivity(intent);//启动目标活动
}
});二、隐式Intent
在配置文件AndroidManifest.xml中,配置一些activity的参数,通过这些行为参数来让Android来判断是否启动这个活动。需要同时满足这些条件才会激活这个活动。category是可以添加多个的。
<activity android:name=".Main23Activity"> <intent-filter> <action android:name="group.chicai.study.ACTION_START"></action> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> </activity>
Button button4 = (Button) findViewById(R.id.button4);
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("group.chicai.study.ACTION_START");
intent.addCategory("android.intent.category.DEFAULT");
startActivity(intent);
}
});
点击Android的返回键就可以销毁当前的活动了。还有一个方法就是当前的活动中调用finish()方法。
Button button2 = (Button)findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
在res/layout下右键,新建一个Layout resource file文件命名为first_layout,点击打开文件,然后拖动一个Button到界面上。在左下角点击Text切换的源码。
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button" android:layout_width="115dp" android:layout_height="67dp" android:text="Button" tools:layout_editor_absoluteX="135dp" tools:layout_editor_absoluteY="140dp" /> </android.support.constraint.ConstraintLayout>
这时候就添加了一个名字为button的按钮了,然后我们在活动中使用这个新建的视图,并且获取到这个按钮,为他添加一个监听事件。
package group.chicai.study;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置这个活动的视图为first_layout,其实是传入一个id到R.id上,随后通过这个获取元素
setContentView((R.layout.first_layout));
//通过R.id获取到按钮,返回为View,强制转换为Button
Button button1 = (Button) findViewById(R.id.button);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(Main2Activity.this,"一段提示文字",Toast.LENGTH_SHORT).show();
}
});
}
}
Android中的日志工具是Log(android.util.Log),这个类提供了5个方法来打印日志。
Log.v(),级别verbose,打印一些意义最小的日志。
Log.d(),打印调试信息,对应级别debug。
Log.i(),用户打印一些比较重要的数据,级别info。
Log.w(),打印一些警告信息,级别warn。
Log.e(),打印程序的错误信息,级别error。
这些方法都要传入两个参数,第一个参数为tag(标签名字,用来检索日志使用),第二个参数是msg就是需要打印的信息了。
打开app/src/main下的AndroidManifest.xml文件,可以看到一个Activity的存在,所有的活动不在AndroidManifest.xml注册是不可以使用的。
<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
这段代码表示对MainActivity这个活动进行注册,intent-filter里面的两行代码表示MainActivity是这个项目的主活动,在手机上点击应用图标,首先启动的就是这个活动。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//设置视图,活动和视图分离
}
}MainActivity继承AppCompatActivity,这是一种向下兼容的Activity。MainActivity中有一个onCreate()方法,这个方法是一个活动被创建时必定要执行的方法。
主要流程是:调用统一下单接口获取到预付订单id,在构造参数调起微信支付,支付成功、支付失败、支付取消都会调起WXPayEntryActivity回调再回到Unity中。然后在Unity中判断状态做出相应的提示,若支付成功要向后台去查询结果。(微信文档流程)
统一下单接口调用和调起微信支付的参数都需要后台去获取,所以测试的时候可以直接使用 地址 获取到json参数来调起微信支付。
在微信支付文档中的资源下载/Android资源下载中下载范例代码,工程里面有一个lib目录,里面有两个jar包(wechat-sdk-android-with-mta-1.0.2.jar和libammsdk.jar),把libammsdk.jar直接拖到Unity的Plugins/Android目录下,就可以使用com.tencent.mm.opensdk.openapi的类了。
Android调起微信支付:
[Serializable]
public class GetPrePayData
{
public int status;
public string appid;
public string partnerid;
public string package;
public string noncestr;
public string timestamp;
public string prepayid;
public string sign;
}
Dictionary<string, string> input = new Dictionary<string, string>();
public void WechatZhifu(string jsonData)
{
AndroidJavaClass androidClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject androidObj = androidClass.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaClass apiFactory = new AndroidJavaClass("com.tencent.mm.opensdk.openapi.WXAPIFactory");
//mWeixinPayApi保存引用后可以一直调用,createWXAPI方法第一个参数为Unity的活动Activity,第二个参数为微信支付的appid
AndroidJavaObject mWeixinPayApi = apiFactory.CallStatic<AndroidJavaObject>("createWXAPI", androidObj, WxPayConfig.GetConfig().GetAppID());
input.Clear();
GetPrePayData data = new GetPrePayData();
try
{
data = JsonUtility.FromJson<GetPrePayData>(jsonData);
}
catch (Exception e)
{
Debug.LogError("微信支付统一接口数据错误");
}
input.Add("appId", data.appid);
input.Add("partnerId", data.partnerid);
input.Add("prepayId", data.prepayid);
input.Add("packageValue", data.package);
input.Add("nonceStr", data.noncestr);
input.Add("timeStamp", data.timestamp);
AndroidJavaObject request = new AndroidJavaObject("com.tencent.mm.opensdk.modelpay.PayReq");
foreach (var kv in input)
request.Set<string>(kv.Key, kv.Value.ToString());//设置参数
request.Set<string>("sign", data.sign);
mWeixinPayApi.Call<bool>("sendReq", request);//调起微信支付
}支付回调:要为应用包名创建一个wxapi/WXPayEntryActivity.java供微信调用回调方法。新建一个AndroidStudio工程,添加一个library模块,包名设置为和Unity项目应用的一样。将unity的jar包和示例文件中的libs的jar包添加到这个模块中。然后在示例文件中,复制wxapi/WXPayEntryActivity.java和Constants.java到模块/src/main/java/com...包名/下,注意修改Constants.java的APP_ID为你的应用ID,修改WXPayEntryActivity.java中包名为你应用包名,去掉视图,修改回调方法去通知Unity的某个物体的某个方法并将微信的返回代码返回给Unity,结束这个Activity就行了。
package com.zfzx.cyzx.wxapi;
import com.zfzx.cyzx.Constants;
import com.zfzx.cyzx.R;
import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.unity3d.player.UnityPlayer;
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler{
private static final String TAG = "MicroMsg.SDKSample.WXPayEntryActivity";
private IWXAPI api;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.pay_result);
api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);
api.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq req) {
}
@Override
public void onResp(BaseResp resp) {
Log.d("jjjjjj", "onPayFinish, errCode = " + resp.errCode);
UnityPlayer.UnitySendMessage("WechatPayRespGameObject","OnWechatPayResp",String.valueOf(resp.errCode));
finish();
}
}把这个Android模块编译成jar包,然后扔进Unity的Plugins/Android目录下。随后在Unity中新建一个物体名称为WechatPayRespGameObject,任意挂上一个脚本,声明一个方法叫OnWechatPayResp接受调,然后根据返回代码做出相应的处理。
//Android的支付回调
private void OnWechatPayResp(string code)
{
if (HallTransfer.Instance == null) return;
switch (code)
{
case "0":
HallTransfer.Instance.cnTipsBox("充值成功", 3);
break;
case "-1":
HallTransfer.Instance.cnTipsBox("充值失败", 3);
break;
case "-2":
HallTransfer.Instance.cnTipsBox("用户取消", 3);
break;
}
}对了,还需要修改AndroidManifest.xml,添加一个Activity
<activity android:name=".wxapi.WXPayEntryActivity" android:exported="true"></activity>
.gradle和.idea是自动生成的一些文件,无须关系。app为项目中的代码和资源,开发基本是围绕这个目录开发。build包含编译时自动生成的文件,无须关心。gradle这个目录下包含了gradle warpper的配置文件,使用gradle wrapper的方式不需要提前将gradle下载好,而是会自动根据本地的缓存情况决定是否需要联网下载gradle。
app下的项目目录:build和上层的build类似。libs为项目放置使用到的第三方jar包。src/main/java放置所有Java代码的地方。src/main/res放置所有的图片、布局、字符串等资源,图片放到drawable目录下,布局放在layout目录下,字符串放在values目录下。AndroidManifest.xml是整个Android项目的配置文件,程序定义的四大组件都需要在这个文件里注册,可以为应用添加权责声明。
活动(Activity)是所有Android应用程序的门面,凡是在应用中你看到的东西都是放在活动中的。
服务(Service)会在后台默默地运行,即使用户退出了应用,服务仍然是可以继续运行的。
广播接收器(Broadcast Receiver)运行你的应用接收来自各处的广播消息,如电话短信等,当然也可以向外发出广播消息。
内容提供器(Content Provider)则为应用程序之间共享数据提供了可能,比如读取系统电话簿中的联系人。
//Java UpdateUtil.java
package com.cdx.unity3d;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.v4.content.FileProvider;
import java.io.File;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import com.unity3d.player.UnityPlayer;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class UpdateUtil {
private Context mContext;
private String apkUrl;
private String savePath;
private String saveFileName;
private String downloadMsg;
private int progress;
private ProgressDialog downloadDialog;
private Thread downLoadThread;
private boolean interceptFlag = false;
public UpdateUtil(Context context)
{
this.mContext = context;
}
/**
* 更新下载apk
*/
public static void downLoadApk(final String url, final String storge, Boolean showNotify) {
final Activity activity = UnityPlayer.currentActivity;
activity.runOnUiThread(new Runnable()
{
public void run()
{
UpdateUtil updater = new UpdateUtil((Context)activity);
updater.Download(url, storge);
}
});
}
public void Download(String url, String storge)
{
this.apkUrl = url;
this.savePath = storge;
this.saveFileName = storge;
this.downloadDialog = new ProgressDialog(this.mContext);
this.downloadDialog.setTitle("游戏更新");
this.downloadDialog.setMessage("请稍后。。。");
this.downloadDialog.setProgressStyle(1);
this.downloadDialog.setCancelable(false);
this.downloadDialog.setCanceledOnTouchOutside(false);
this.downloadDialog.setMax(100);
this.downloadDialog.show();
downloadApk();
}
private void downloadApk()
{
this.downLoadThread = new Thread(this.mdownApkRunnable);
this.downLoadThread.start();
}
private Runnable mdownApkRunnable = new Runnable()
{
public void run()
{
try
{
URL url = new URL(UpdateUtil.this.apkUrl);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.connect();
int length = conn.getContentLength();
InputStream is = conn.getInputStream();
File file = new File(UpdateUtil.this.saveFileName);
if (file.isFile()) {
file = new File(file.getParent());
}
if ((file.isDirectory()) && (!file.exists())) {
file.mkdir();
}
String apkFile = UpdateUtil.this.saveFileName;
File ApkFile = new File(apkFile);
FileOutputStream fos = new FileOutputStream(ApkFile);
String fName = UpdateUtil.this.apkUrl.trim();
fName = fName.substring(fName.lastIndexOf("/") + 1,fName.lastIndexOf("?"));
UpdateUtil.this.downloadMsg = ("正在下载文件:" + fName);
UpdateUtil.this.mHandler.sendEmptyMessage(0);
int count = 0;
byte[] buf = new byte[1024];
do
{
int numread = is.read(buf);
count += numread;
UpdateUtil.this.progress = ((int)((float)count / (float) length * 100.0F));
UpdateUtil.this.downloadMsg = ("正在下载文件:" + fName + " " + count / 1024 + "kb/" + length / 1024 + "kb");
UpdateUtil.this.mHandler.sendEmptyMessage(1);
if (numread <= 0) {
break;
}
fos.write(buf, 0, numread);
} while (!UpdateUtil.this.interceptFlag);
fos.close();
is.close();
UpdateUtil.this.mHandler.sendEmptyMessage(2);
}
catch (MalformedURLException e)
{
e.printStackTrace();
UpdateUtil.this.downloadMsg = ("下载失败,出现异常:" + e.getMessage());
UpdateUtil.this.mHandler.sendEmptyMessage(3);
}
catch (IOException e)
{
e.printStackTrace();
UpdateUtil.this.downloadMsg = ("下载失败,出现异常:" + e.getMessage());
UpdateUtil.this.mHandler.sendEmptyMessage(3);
}
}
};
private Handler mHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch (msg.what)
{
case 0:
UpdateUtil.this.downloadDialog.setMessage(UpdateUtil.this.downloadMsg);
break;
case 1:
UpdateUtil.this.downloadDialog.setMessage(UpdateUtil.this.downloadMsg);
UpdateUtil.this.downloadDialog.setProgress(UpdateUtil.this.progress);
break;
case 2:
UpdateUtil.this.installApk();
UpdateUtil.this.downloadDialog.dismiss();
break;
case 3:
UpdateUtil.this.downloadDialog.dismiss();
AlertDialog.Builder builder = new AlertDialog.Builder(UpdateUtil.this.mContext);
builder.setTitle("下载失败");
builder.setMessage("是否重试?");
builder.setPositiveButton("确定", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
UpdateUtil.this.Download(UpdateUtil.this.apkUrl, UpdateUtil.this.savePath);
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
Activity act = (Activity)UpdateUtil.this.mContext;
act.finish();
}
});
builder.create().show();
break;
}
}
};
/**
* 安装apk
*/
private void installApk() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri data = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//判断版本大于等于7.0
// 生成文件的uri,,
// 注意 下面参数com.xxx.xxx.fileprovider为apk的包名加上.fileprovider,
data = FileProvider.getUriForFile(this.mContext, "com.xxx.xxx.fileprovider", new File(this.saveFileName));
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 给目标应用一个临时授权
} else {
data = Uri.fromFile(new File(this.saveFileName));
}
intent.setDataAndType(data, "application/vnd.android.package-archive");
this.mContext.startActivity(intent);
}
}修改Plugins/Android/AndroidManifest.xml文件,添加权限和provider
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.zfzx.cyzx.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths"/> </provider>
在Plugins/Android/res/xml下添加filepaths.xml文件,注意替换包名!
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!--<external-path name="external_files" path="."/>--> <external-path path="Android/data/包名!" name="files_root" /> <external-path path="." name="external_storage_root" /> </paths>
这样把下载的apk的文件保存在Application.persistentDataPath目录下,也就是在downloadApk方法中传入的storge参数为Application.persistentDataPath + "/包.apk"就可以了。如果打包不了或报错了,将android-support-v4.jar放到Plugins/Android目录下。