Android Studio中各个版本的说明
compileSdkVersion:
compileSdkVersion告诉 Gradle 用哪个 Android SDK 版本编译你的应用。使用任何新添加的 API 就需要使用对应等级的 Android SDK。
需要强调的是修改 compileSdkVersion 不会改变运行时的行为。当你修改了 compileSdkVersion 的时候,可能会出现新的编译警告、编译错误,但新的 compileSdkVersion 不会被包含到 APK 中:它纯粹只是在编译的时候使用。(应该修复这些警告,他们的出现是有原因的)
因此推荐使用最新的 SDK 进行编译。在现有代码上使用新的编译检查可以获得很多好处,避免新弃用的 API ,并且为使用新的 API 做好准备。
minSdkVersion:
如果 compileSdkVersion 设置为可用的最新 API,那么minSdkVersion 则是应用可以运行的最低要求。minSdkVersion 是Google Play商店用来判断用户设备是否可以安装某个应用的标志之一。 你所使用的库,如 Support Library 或 Google Play services,可能有他们自己的 minSdkVersion 。你的应用设置的 minSdkVersion 必须大于等于这些库的 minSdkVersion 。例如有三个库,它们的 minSdkVersion 分别是 4, 7 和 9 ,那么你的 minSdkVersion 必需至少是 9 才能使用它们。
targetSdkVersion:
targetSdkVersion 是 Android 提供向前兼容的主要依据,在应用的 targetSdkVersion 没有更新之前系统不会应用最新的行为变化。这允许你在适应新的行为变化之前就可以使用新的 API。
由于某些行为的变化对用户是非常明显的(弃用的 menu 按钮,运行时权限等),所以将 target 更新为最新的 SDK 是所有应用都应该优先处理的事情。但这不意味着你一定要使用所有新引入的功能,也不意味着你可以不做任何测试就盲目地更新 targetSdkVersion ,请一定在更新 targetSdkVersion 之前做测试!你的用户会感谢你的。
通俗点说,就是你可以通过最新的compileSdkVersion 编译应用,但是在targetSdkVersion没有更新之前,API的行为就是targetSdkVersion 行为,而不是compileSdkVersion 的行为;
gradle:
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。
隐藏标题栏和状态栏
-
方法一:注意若想有效需要继承自Activity而不是AppCompatActivity
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate( savedInstanceState ); requestWindowFeature(Window.FEATURE_NO_TITLE);//一定要在setContentView()之前执行,不然会报错 setContentView( R.layout.activity_main ); }
-
方法二
style文件中:Manifast.xml文件中
//在theme中加进去
AndroidManifast.xml中配置首页
//主要是添加下面标签和其中的两句话
随后消失的通知
参数1:上下文,因为本身就是一个context,所以传入一个activity即可
参数2:显示文本
参数3:显示时长,有两个内置参数Toast.LENGTH_SHORT和Toast.LENGTH_LONG
Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();
菜单项
在res目录下新建menu文件夹然后新建xml文件,添加一下代码
重写onCreateOptionsMenu方法,用于生成菜单
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.main,menu);
return true;//返回false菜单无法显示
}
点击事件重写onOptionsItemSelected方法
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.add_item:
Toast.makeText(this,"增加",Toast.LENGTH_LONG).show();
break;
case R.id.delete_item:
Toast.makeText(MainActivity.this,"删除",Toast.LENGTH_SHORT).show();
break;
default:
}
return true;
}
活动
- 销毁 finish()
- Intent
- 隐式
- 显式
- 向上/下活动传递数据
- 实践:知晓当前是哪一个活动,随时随地退出程序,启动活动的最佳写法
显式使用Intent跳转就2行代码
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
同款隐式调用
配置AndroidManifest.xml
然后在跳转处还是两行
Intent intent = new Intent("android.intent.action.ACTION_START");
startActivity(intent);
其他intent的隐式调用
①使得跳转浏览器
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://www.baidu.com"));
startActivity(intent);
②双重相应 在上述代码的基础上,如果需要其他Activity也相应,则配置AndroidManifast.xml
//配置响应值为intent.ACTION_VIEW
//相应数据协议是https,使得可以响应打开网页的Intent
###一键退出以及活动管理
让所有的activity继承自一个基类BaseActivity
public class BaseActivity extends Activity {
@Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
ActivityList.addItem(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityList.removeLastActivity(this);
}
}
编写一个类用于管理活动栈ActivityList
public class ActivityList {
private static ArrayList activityArrayList = new ArrayList<>();
public static ArrayList getActivityArrayList() {
return activityArrayList;
}
public static void addItem(Activity activity){
activityArrayList.add(activity);
}
public static void removeLastActivity(Activity activity){
activityArrayList.remove(activity);
}
public static void removeAllActivity(){
for (Activity activity : activityArrayList){
if (!activity.isFinishing()){
activity.finish();
}
}
}
}
在需要一键退出的地方调用removeAllActivity()方法
Button btn = findViewById(R.id.exit_btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityList.removeAllActivity();//调用销毁方法
}
});
Fragment
- f-f通讯
- f-a通讯
- a-f通讯
- fragment入栈 FragmentTransaction提供了addToBackStack()方法
- fragment动态替换 注意:替换的布局如果是垂直的线性布局的话,新的fragment将会显示在替换的布局下方,最好使用相对布局
- 生命周期
- 动态选择双叶或单页模式(手机和平板之间切换)
- layout/activity_main和layout-large/activity_main 这样是不是不能有activity的跳转?
- layout-sw600dp/activity_main和layout/activity_main 定值限制
- 实战:新闻页面 p180
动态替换fragment并入栈
MainActicity.java(关键部分!)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.transFrag);
button.setOnClickListener(this);
}
@Override
public void onClick(View view) {
System.out.println("点击");
AnotherFragment fragment = new AnotherFragment();
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.layout_Trans,fragment);
transaction.commit();
}
对应的xml
LeftFragment.java/RightFragment.java/AnotherFragment.java代码类似
public class LeftFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.left_fragment,container,false);
return view;
}
}
LeftFragment.java对应的xml
RightFragment.java/AnotherFragment.jav(类似)对应的xml
广播
- 动态/静态监听
- 监听系统网络变化 p190(别忘了配置权限)
- 动态:只能在程序启动之后才能接收广播
- 实战:强制下线
动态注册监听网络变化
MainActivity.java代码
public class MainActivity extends AppCompatActivity {
private MyReceiver receiver;//继承自BoradcastReceiver的类,用于相应监听事件
private IntentFilter intentFilter;//类似于监听器
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
receiver = new MyReceiver();
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");//给监听器添加监听事件
registerReceiver(receiver,intentFilter);//绑定监听器和接受者
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);//动态注册的广播一定都要取消注册
}
}
MyReceiver.java (其实可以写成MainActivity.java的内部类)
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if(networkInfo!=null){
Toast.makeText(context,"network is available!",Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context,"network is not available!",Toast.LENGTH_SHORT).show();
}
}
}
记得在AndroidManifast.xml中声明权限,Android系统为了安全性做了规定,如果程序要访问一些系统关键信息,必须在配置文件中声明权限才可以
AndroidManifast.xml
静态注册监听
静态注册的监听即使程序为运行状态下也可以接受广播
接受者代码不变,只不过捆绑写在了配置文件中
AndroidManifast.xml配置
本地广播
MainActivity.java
public class MainActivity extends AppCompatActivity {
private MyReceiver receiver;
private IntentFilter intentFilter;
private LocalBroadcastManager localBroadcastManager;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
receiver = new MyReceiver();
btn = findViewById(R.id.send_msg);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//设置监听器
intentFilter = new IntentFilter();
//广播管理
localBroadcastManager = LocalBroadcastManager.getInstance(MainActivity.this);
//发送者
Intent intent = new Intent("com.example.boardcastdemo.LOCAL_BOARDCAST");
//发送
localBroadcastManager.sendBroadcast(intent);
//监听器添加监听源
intentFilter.addAction("com.example.boardcastdemo.LOCAL_BOARDCAST");
//绑定接受者和监听器
localBroadcastManager.registerReceiver(receiver,intentFilter);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(receiver);
}
}
本地广播实现强制退出登录
这里需要进行详细阐述!有坑!有大坑!就是关于权限和SDK版本的问题
不考虑权限或者版本问题,很可能会出现以下错误,例如按照Android4.0的版本的《第一行代码》中的案例敲Android8.0的项目,
android.view.WindowManager$BadTokenException: Unable to add window — token android.os.BinderProxy@447a6748 is not valid; is your activity running?
或者
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootlmpl$W@40ec8528 -- permission denied for this window
解决方案
Android 8.0 除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更。其中有些是针对Android 8.0 的应用。官方文档描述如下。
这些行为变更专门应用于针对 O 平台或更高平台版本的应用。针对 Android8.0 或更高平台版本进行编译,或将 targetSdkVersion设为 Android 8.0 或更高版本的应用开发者必须修改其应用以正确支持这些行为(如果适用)。
提醒窗口
使用SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:
•TYPE_PHONE
•TYPE_PRIORITY_PHONE
•TYPE_SYSTEM_ALERT
•TYPE_SYSTEM_OVERLAY
•TYPE_SYSTEM_ERROR
相反,应用必须使用名为 TYPE_APPLICATION_OVERLAY 的新窗口类型。
使用TYPE_APPLICATION_OVERLAY 窗口类型显示应用的提醒窗口时,请记住新窗口类型的以下特性:
•应用的提醒窗口始终显示在状态栏和输入法等关键系统窗口的下面。
•系统可以移动使用 TYPE_APPLICATION_OVERLAY窗口类型的窗口或调整其大小,以改善屏幕显示效果。
•通过打开通知栏,用户可以访问设置来阻止应用显示使用 TYPE_APPLICATION_OVERLAY 窗口类型显示的提醒窗口。
……
因此弹窗需要作出版本检测
if (Build.VERSION.SDK_INT>=26) {//8.0新特性
mWindowParams.type= WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
}else{
mWindowParams.type= WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
对于权限问题来说
如果是在Android4.x的情况,只用在AndroidManifest.xml中添加权限:
(该权限在8.0代码中即使不加也可以正常运行,所以暂时不需要)
如果是在Android6(API 23)以上,则还需添加以下代码进行请求权限:
//权限判断
if (Build.VERSION.SDK_INT >= 23) {
if(!Settings.canDrawOverlays(getApplicationContext())) {
//启动Activity让用户授权
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName()));
startActivity(intent);
return;
} else {
//执行6.0以上绘制代码
}
} else {
//执行6.0以下绘制代码、
}
或者
//权限判断
if (Build.VERSION.SDK_INT >= 23) {
if(!Settings.canDrawOverlays(getApplicationContext())) {
//启动Activity让用户授权
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName()));
startActivityForResult(intent,10);
return;
} else {
//执行6.0以上绘制代码
}
} else {
//执行6.0以下绘制代码
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 10) {
if (Build.VERSION.SDK_INT >= 23) {
if (!Settings.canDrawOverlays(this)) {
// SYSTEM_ALERT_WINDOW permission not granted...
Toast.makeText(LoginActivity.this,"not granted",Toast.LENGTH_SHORT);
}
}
}
}
①方式一
配合Activity管理
ActivityList.java
public class ActivityList {
private static ArrayList activityArrayList = new ArrayList<>();
public static ArrayList getActivityArrayList() {
return activityArrayList;
}
public static void addItem(Activity activity){
activityArrayList.add(activity);
}
public static void removeLastActivity(Activity activity){
activityArrayList.remove(activity);
}
public static void removeAllActivity(){
for (Activity activity : activityArrayList){
if (!activity.isFinishing()){
activity.finish();
}
}
}
}
同意实现一个Activity的共同父类,BaseActivity.java
public class BaseActivity extends Activity {
private Receiver receiver;
@Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
ActivityList.addItem(this);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("come.example.yt.broadcastbestpractice.FORCE_OFFLINE");
receiver=new Receiver();
registerReceiver(receiver,intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityList.removeLastActivity(this);
}
}
需要发送广播的类
public class ThirdActivity extends BaseActivity {
LocalBroadcastManager manager ;
IntentFilter intentFilter ;
Intent intent ;
Receiver receiver ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
manager = LocalBroadcastManager.getInstance(ThirdActivity.this);
intentFilter = new IntentFilter();
intent = new Intent("com.activitymanagedemo.LOGIN");
receiver = new Receiver();
intentFilter.addAction("com.activitymanagedemo.LOGIN");
manager.registerReceiver(receiver,intentFilter);
Button btn = findViewById(R.id.exit_btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityList.removeAllActivity();
}
});
Button sendBtn = findViewById(R.id.sendMsg_btn);
sendBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
permission(ThirdActivity.this);
}
});
}
//权限判断并获取(Android6(API 23)以上需要)
public void permission(Context context){
if (Build.VERSION.SDK_INT>=23) {
if(!Settings.canDrawOverlays(context)) {
Uri uri = Uri.parse("package:" + context.getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, uri);
//不使用startActivity的原因是因为需要回调函数onActivityResult
startActivityForResult(intent,10);
return;
}else{
manager.sendBroadcast(intent);
}
}else {
manager.sendBroadcast(intent);
}
}
//回到应用时触发的事件
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 100) {
if (Build.VERSION.SDK_INT >= 23 ) {
if (!Settings.canDrawOverlays(ThirdActivity.this)) {
Toast.makeText(ThirdActivity.this, "Permisson denied", Toast.LENGTH_SHORT).show();
}
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
manager.unregisterReceiver(receiver);
}
}
接受者
public class Receiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
//Toast.makeText(context,"接受到通知",Toast.LENGTH_SHORT).show();
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setCancelable(false);
alert.setTitle("提示");
alert.setMessage("您的账号在其他设备登录!");
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
ActivityList.removeAllActivity();
Intent intent = new Intent(context,MainActivity.class);
//广播弹窗所需
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
});
//广播弹窗所需
AlertDialog dialog = alert.create();
//广播弹窗所需
if (Build.VERSION.SDK_INT >= 26){
//安卓8.0以上需要,注意版本判断,
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}else {
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
}
dialog.show();
}
}
②方式二(不推荐)
依然配合Activity管理
ActivityList.java
public class ActivityList {
private static ArrayList activityArrayList = new ArrayList<>();
public static ArrayList getActivityArrayList() {
return activityArrayList;
}
public static void addItem(Activity activity){
activityArrayList.add(activity);
}
public static void removeLastActivity(Activity activity){
activityArrayList.remove(activity);
}
public static void removeAllActivity(){
for (Activity activity : activityArrayList){
if (!activity.isFinishing()){
activity.finish();
}
}
}
}
同意实现一个Activity的共同父类,BaseActivity.java
public class BaseActivity extends Activity {
private Receiver receiver;
@Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
ActivityList.addItem(this);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("come.example.yt.broadcastbestpractice.FORCE_OFFLINE");
receiver=new Receiver();
registerReceiver(receiver,intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityList.removeLastActivity(this);
}
//广播接收者写成内部类的形式,这样才能在高版本安卓中绕过权限相关的配置,
但是这样在基类中onResume()方法使得每一个子类都生成接受者感觉会对性能有很大的损耗
class Receiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
//Toast.makeText(context,"接受到通知",Toast.LENGTH_SHORT).show();
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setCancelable(false);
alert.setTitle("提示");
alert.setMessage("您的账号在其他设备登录!");
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
ActivityList.removeAllActivity();
Intent intent = new Intent(context,MainActivity.class);
context.startActivity(intent);
}
});
alert.show();
}
}
}
需要发送广播的类
public class SecondActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Button btn = findViewById(R.id.offline_btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent i=new Intent("come.example.yt.broadcastbestpractice.FORCE_OFFLINE");
sendBroadcast(i);
}
});
}
}
异步
- 两种方式
- 显式
- 隐式
- 异步更新UI的方式
- 其中用到的handle是不是和广播一样?
与handle相伴的是message,message用于线程之间传递少量数据,handle专门用于接收处理、发送message。
多线程
①其实就是实现一个类继承自Thread,然后重写run()方法
class MyThread extends Thread {
@Override
public void run(){
//任务。。。
}
}
然后需要启动就实例化然后调用start()方法
new MyThread().start();
②使用接口
class MyThread extends Thread {
@Override
public void run(){
//任务。。。
}
}
启动方式有所不同
MyThread myThread = new MyThread();
new Thread(myThread).start();
③匿名类的方式
new Thread(new Runnable() {
@Override
public void run(){
//任务。。。
}
}).start();
服务
需要在AndroidManifest.xml中进行注册才能生效
- 重写方法
- onBind
- onCreate
- onStartCommand()服务启动的时候调用
- onDestroy
- 服务和活动之前通讯(一堆binder)
网络
- WebView
- WebView标签
- AndroidManifest.xml的配置
- HttpURLConnection请求 382
- 线程中
- 初始化请求体
- url
- 请求方式
- 相应时间
- 获取输入流
- 对输入流进行读取。。。
- HttpClient
线程内根据网络请求更新UI
public class MainActivity extends Activity implements View.OnClickListener {
private static final int SHOW_RES = 0;
Button sendReq;
TextView showRes;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case SHOW_RES:
String response = (String) msg.obj;
showRes.setText(response);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sendReq = findViewById(R.id.send_btn);
showRes = findViewById(R.id.show_text);
sendReq.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.send_btn){
sendRequest();
}
}
public void sendRequest(){
new Thread(new Runnable() {
@Override
public void run() {
HttpsURLConnection connection = null;
try {
URL url = new URL("https://www.baidu.com");
connection =(HttpsURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
InputStream in = connection.getInputStream();
//对获取数据进行读取
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuffer response = new StringBuffer();
String line;
while ((line = reader.readLine())!=null){
response.append(line);
}
Message message = new Message();
message.what = SHOW_RES;
message.obj = response.toString();
handler.sendMessage(message);
}catch (Exception e){
e.printStackTrace();
}finally {
if(connection!=null){
connection.disconnect();
}
}
}
}).start();
}
}