导读
1.一些重要的Studio技巧
2.Android 6.0动态权限申请
3.对Handler的说明
4.使用Handler的实现和优化
5.打地鼠案例
一些重要的Studio技巧
⚠️查看一个类的快捷键 command+单击
⚠️快速将局部变量变成全局变量 command+alt+t
⚠️更多技巧见 Android 之路31—关于AndroidStudio的1,2,3
Android 6.0动态权限申请
对Handler的说明
MessageQueue是一个线程队列,里边是要执行的线程,而Looper相当于一个抽水机,把线程从队列里抽出来,抽出的线程交给handlerMessage处理,或者交给run去执行,所以handler就是处理不同线程的一种机制
子线程使用Handler要如下编写
Looper.prepare();
Handler handler=new Handler();
Looper.loop();
使用Handler的实现和优化
Handler常用方法
activity_main.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">
<TextView
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="Emilia Clarke"
android:textSize="28sp"
android:gravity="center"
android:textColor="#ffffff"
android:background="#3f51b5"
android:paddingRight="15dp"/>
<Button
android:id="@+id/bt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="点击"/>
</LinearLayout>
MainActivity.java
package com.hala.view01;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
/*
线程间通信
1。主线程(UI线程)执行轻量级任务
2。new 的新线程 执行下载等耗时任务
3。下载完成后通知主线程进行后续操作
*/
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tx=(TextView)findViewById(R.id.header);
//创建Handler
final Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//处理消息
Log.d(TAG,"handleMessage:"+msg.what);
if(msg.what==1002){
tx.setText(""+msg.obj);
}
}
};
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//有可能消耗大量时间,比如下载
new Thread(new Runnable() {
@Override
public void run() {
/* try {
//这里用休眠来表示耗时操作
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
//发送空消息,只有一个代号但没有实质内容
handler.sendEmptyMessage(1001);
//以下都是android系统写好的参数
Message message=Message.obtain();
message.what=1002;
message.arg1=1;
message.arg2=2;
message.obj="Neymar";
handler.sendMessage(message);
//两个方法实现效果相同,但方法不同 handler.sendMessageAtTime(message,SystemClock.uptimeMillis()+3000);
handler.sendMessageDelayed(message,3000);
}
}).start();
}
});
}
}
java强引用与弱引用的说明
Handler防止内存泄漏的优化以及倒计时实现
Manifest文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hala.view01">
<!-- 网络链接,写文件和读文件的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"></activity>
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
布局文件 activity_main2
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hala.view01.Main2Activity">
<TextView
android:id="@+id/textView"
android:layout_width="111dp"
android:layout_height="71dp"
android:text="TextView"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
tools:layout_editor_absoluteY="194dp"
android:gravity="center"
android:textSize="20sp"/>
</android.support.constraint.ConstraintLayout>
Main2Activity
package com.hala.view01;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.lang.ref.WeakReference;
public class Main2Activity extends AppCompatActivity {
private TextView tv;
public static final int MIN = 0;
public static final int MAX = 10;
public static final int Code = 10001;
public static final int sendTime = 1000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
tv=(TextView)findViewById(R.id.textView);
//这里参数要传上下文
wHandler handler=new wHandler(this);
Message message=Message.obtain();
message.what=Code;
message.arg1=MAX;
handler.sendMessageDelayed(message,sendTime);
}
/**
* 这里是自己创建的静态Handler的弱引用,目的是防止内存泄露
*/
public static class wHandler extends Handler{
final WeakReference<Main2Activity> mWeaker;
public wHandler(Main2Activity activity) {
mWeaker=new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//此句获取了当前Activity的弱引用
Main2Activity activity=mWeaker.get();
switch (msg.what){
case Code:
int value=msg.arg1;
activity.tv.setText(String.valueOf(value--));
//这里是一个循环发送直到达到条件
if (value>= MIN) {
//这里Message必须要从消息池里再取一个,目的是将value发回arg1去
Message message=Message.obtain();
message.what=Code;
message.arg1=value;
sendMessageDelayed(message,sendTime);
}
break;
}
}
}
}
显示结果
下载文件时Handler的运用
配置文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hala.view01">
<!-- 网络链接,写文件和读文件的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Main2Activity">
</activity>
</application>
</manifest>
布局文件
<?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">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="50dp"
android:max="100"/>
<Button
android:id="@+id/bt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="点击"/>
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"/>
</LinearLayout>
java文件
package com.hala.view01;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
public class MainActivity extends Activity {
public static final String APP_URL = "http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk";
public static final int INT1 = 1002;
ProgressBar pb=(ProgressBar)findViewById(R.id.progressBar);
TextView tv=(TextView)findViewById(R.id.tv);
Handler M_HANDLER;
public static final int INT = 1001;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
1.主线程->start
2.点击按键发起下载
3.开启子线程做下载
4.下载过程中通知主线程更新进度条
*/
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
download(APP_URL);
}
}).start();
}
});
M_HANDLER = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case INT:
pb.setProgress((Integer)msg.obj);
break;
case INT1:
tv.setText("下载失败");
break;
}
}
};
}
private void download(String appUrl){
try {
URL url=new URL(appUrl);
URLConnection urlConnection=url.openConnection();
InputStream inputStream=urlConnection.getInputStream();
//获取文件的总长度为
int contentLength=urlConnection.getContentLength();
//创建下载的文件
String downloadFolderName= Environment.getExternalStorageDirectory()+File.separator+"Emilia Clarke"+File.separator;
File file=new File(downloadFolderName);
if(!file.exists()){
file.mkdir();
}
String fileName=downloadFolderName+"emilia.apk";
File apkfile=new File(fileName);
if(apkfile.exists()){
apkfile.delete();
}
int downloadSize=0;
byte[] b=new byte[1024];
int length;
OutputStream outputStream=new FileOutputStream(fileName);
while((length=inputStream.read(b))!=-1){
outputStream.write(b,0,length);
downloadSize+=length;
Message message=Message.obtain();
message.obj=downloadSize*100/contentLength;
message.what= INT;
M_HANDLER.sendMessage(message);
}
inputStream.close();
outputStream.close();
} catch (MalformedURLException e) {
notifyDownloadFile();
e.printStackTrace();
} catch (IOException e) {
notifyDownloadFile();
e.printStackTrace();
}
}
private void notifyDownloadFile() {
Message message=Message.obtain();
message.what= INT1;
M_HANDLER.sendMessage(message);
}
}
打地鼠案例
布局文件
?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hala.view01.Main2Activity">
<RelativeLayout
android:id="@+id/rl"
android:layout_width="368dp"
android:layout_height="439dp"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/button">
<ImageView
android:id="@+id/iv"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/timg"
android:visibility="gone"/>
</RelativeLayout>
<Button
android:onClick="onClick"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="0dp"
android:text="开始"
android:id="@+id/button"
app:layout_constraintHorizontal_weight="1"
android:layout_marginStart="8dp" />
<TextView
android:id="@+id/tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="分数"
android:textSize="25sp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="8dp"
app:layout_constraintTop_toTopOf="@+id/button"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintHorizontal_weight="1"
android:layout_marginEnd="8dp"
app:layout_constraintLeft_toRightOf="@+id/button"
android:layout_marginLeft="8dp" />
</android.support.constraint.ConstraintLayout>
java文件
package com.hala.view01;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.Random;
public class Main2Activity extends AppCompatActivity implements View.OnClickListener,View.OnTouchListener {
public static final int Code = 123;
public static final int Rand = 500;
public static final int MAX = 10;
private TextView tv;
private Button bt;
private ImageView iv;
private RelativeLayout rl;
private int total;
private int success;
private DiglettHandler diglettHandler=new DiglettHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
setTitle("打我呀");
initView();
}
private void initView() {
tv = (TextView)findViewById(R.id.tv);
bt = (Button)findViewById(R.id.button);
iv = (ImageView)findViewById(R.id.iv);
rl = (RelativeLayout)findViewById(R.id.rl);
bt.setOnClickListener(this);
iv.setOnTouchListener(this);
//此句容易漏掉
DiglettHandler diglettHandler=new DiglettHandler(this);
}
/**
* 点击按钮开始游戏
* @param v
*/
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button:
start();
break;
}
}
/**
* 开始打第一个地鼠
* 发送消息
*/
private void start() {
total=0;
success=0;
bt.setText("游戏中。。。");
bt.setEnabled(false);
next(0);
}
/**
* 打第一个以后的地鼠
* @param delayTime
*/
private void next(int delayTime){
//Random().nextInt(368)表示生成0~368的随机数,包括0,不包括368
int x=new Random().nextInt(368);
int y=new Random().nextInt(439);
Message message=Message.obtain();
message.what= Code;
message.arg1=x;
message.arg2=y;
diglettHandler.sendMessageDelayed(message,delayTime);
total++;
}
/**
* 游戏结束的反应
*/
private void clear(){
iv.setVisibility(View.GONE);
bt.setText("开始");
bt.setEnabled(true);
}
/**
* 打中地鼠后的反应
* @param v
* @param event
* @return
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
//地鼠为不可见
v.setVisibility(View.GONE);
success++;
return true;
}
public static class DiglettHandler extends Handler{
public final WeakReference<Main2Activity> mWeaker;
public DiglettHandler(Main2Activity activity) {
mWeaker=new WeakReference<Main2Activity>(activity);
}
/**
* 点击屏幕后的处理机制
* @param msg
*/
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Main2Activity activity=mWeaker.get();
switch (msg.what){
case Code:
if(activity.total> MAX){
activity.clear();
return;
}
int x=msg.arg1;
int y=msg.arg2;
activity.iv.setX(x);
activity.iv.setY(y);
//默认为不显示,这里要显示出来
activity.iv.setVisibility(View.VISIBLE);
activity.tv.setText("打到"+activity.success+"只,漏掉"+(activity.total-activity.success)+"只哦");
//传回随机时间,执行next方法
int randomTime=new Random().nextInt(Rand)+ Rand;
activity.next(randomTime);
break;
}
}
}
}
显示结果