我的大学Android笔记

在大学期间,我的Android跌跌撞撞的学了一年。我上学时代对于知识的整理还是蛮痴迷的,可惜当时不会使用markdown(富文本让我头昏脑胀)。如今在利用晚上睡觉、工作之余把大学的Android用markdown整理了一遍。供学弟学妹们参考书使用。

超长篇!!大量小白(学生)操作!!高手止步!!
当然,后续笔记中,我会补充很多企业级写法、常用框架、底层代码、flutter、RN等,以供大家参考!

第一节 基础布局


  • 0.常用属性
layout_width[宽]、layout_height[高] 其属性包含(match_parent填充窗体、wrap_content包裹内容)
layout_marginTop上外边距
layout_weight(设置权重(要实施权重的方向不能设置为填充))
  • 1.线性布局(LinearLayout)
它主要以水平与垂直的方式来显示界面控件。当控件水平排列时(显示从左到右),当控件垂直排列时,显示顺序从上到下。
线性布局中有一个非常重要的属性orientation。用于控制排列方向。(两个值horizontal[水平]于vertical[垂直])

如何让线形布局中的控件居中呢?
layout_gravity属性只有根布局是LinearLayout的情况下才能使用。表示子布局在父布局中的显示方式:
layout_gravity有以下几种值:
center:居中(横向和纵向均居中)
center_horizontal:水平居中
center_vertical:垂直居中
  • 2.相对布局(RelateiveLayout)
相对布局是通过相对定位的方式指定控件位置,在设计的时候要遵循控件之间的依赖关系。其内部的属性较多,比较常用的是以下几个
layout_centerInParent位于父布局中央位置
layout_centerVertical位于父布局垂直居中位置
layout_centerHorizontal位于父布局水平居中位置
layout_above当前控件位于某控件上方
layout_below当前控件位于某控件下方
layout_toLeftOf当前控件位于某控左侧
layout_toRightOf当前控件位于某控右侧
layout_alignBottom和某控件的底部对齐
layout_alignParentBottom当前控件是否与父控件底部对齐
  • 3.帧布局(frameLayout)
帧布局是最简单的一种布局模式,该布局为每一个控件都创造一个空白区域。安装先后顺序重叠摆放(先放入的显示在最底层)
foreground属性设置容器前景(始终在所有子控件至上)
  • 4.表格布局(TableLayout)
表格布局是以表格形式排列控件的,表格布局需要tableRow配合使用,每一行都由tableRow决定
  • 5.绝对布局(AbsoluteLayout)「已经废弃」

  • 6.网格布局(GridLayout)「android4.0出现」

  • 7.约束布局(ConstraintLayout)「维护太费劲」


第二节 Android常用控件


  • 1.TextView 用于显示文字信息
gravity="center"    居中显示
  • 2.EditText 继承于TextView进行可编辑操作(文本框)
hint="提示"      //用于文本框提示
maxLines="1"    //设置最大行数
backgroud="@null"  //不设置会出现一道蜜汁下划线
  • 3.Button 按钮 响应点击事件
/*
 *三种实现方式(java8以后lambda表达式也成为第四种大家较为常用的方式之一)
 */
//1. 用onClick="click"标签 在对应的activity中实现该方法
    Button myBtn_one = (Button)findViewById(R.id.bt_one);
    public void click(View view){//参数很重要
        myBtn_one.setText(按钮已被点击);
    } 
        
//2. 用匿名内部类实现
    myBtn_one.setOnClickListener(new View.OnClickListener(){
        public void onClick(View v){
            myBtn_one.setText(按钮已被点击);
        }
    })
            
//3. 在当前Activity实现OnClickListener接口
    myBtn_one.setOnClickListener(this);
    public void onClick(View v){
        switch(v.getId){
            case R.id.bt_one:
                break;
            default:
                break;
        }
    }
  • 4.RadioButton单选按钮
//RadioButton必须放到RadioGroup中去
    RadioGroup rg=(RadioGroup)findViewById(R.id.bt_two);
    rg.setOnClickedChangedListener(new RadioGroup.OnClickedChangedListener{
         public void onCheckedChanged(RadioGroup rg,int checkedId){
              if(checkedId == R.id.rbtn){
                  //具体操作
              }
         }
     })
  • 5.ImageView 视图控件
    可以从各种来源加载图像,并提供缩放、裁剪、着色等功能。
backgroud="@drawable/bg"//拉伸
src="@drawable/bg"//不拉伸

第三节 常见对话框


对话框是程序与用户交互的一种方式,通常用于显示当前程序提示信息以及相关说明,以小窗口形式展现。常见有以下几种:

  • 1.普通对话框

链式调用:

AlertDialog ad=new AlertDialog.Builder(this)
         .setTitle("Dialog对话框")//设置标题
         .setMessage("是否确定退出")//设置提示信息
         .setIcon(R.mipmap.ic_launcher)//设置图标
         .setPositiveButton("确定",null)//添加确定按钮
         .setNegetiveButton("取消",null)//设置取消按钮
         .create();
ad.show();

2.单选对话框

AlertDialog ad=new AlertDialog.Builder(this)
         .setTitle("请选择性别")//设置标题
         .setIcon(R.mipmap.ic_launcher)//设置图标
         .setPositiveButton("确定",null)//添加确定按钮
         .setSingleChoiceItems(new String[]{"男","女"},0,new DialogInterFace.OnClickListener(){ 
             /*参数0表示默认选择第一个,-1可表示没有默认选中*/
              public void onClick(DialogInterFace Dialog,int which){
              }
          })
         .create();
ad.show();

3.多选对话框

AlertDialog ad=new AlertDialog.Builder(this)
         .setTitle("请选择兴趣爱好")//设置标题
         .setIcon(R.mipmap.ic_launcher)//设置图标
         .setPositiveButton("确定",null)//添加确定按钮
         .setMultiChoiceItems(new String[]{"旅游","美食","汽车","购物"},null,null)
         .create();
ad.show();

4.进度条对话框

ProgressDialog dialog=new ProgressDialog(this);
dialog.setTitle("进度条对话框");
dialog.setIcon(R.mipmap.ic_launcher);
dialog.setMessage("正在下载请稍后")
dialog.setprogressStyle(ProgressDialog.STYLE_HORIZONTAL);//设置样式
dialog.show;

5.消息对话框
Toast.makeText(this,"提示信息",Toast.LENGTH.LONG).show();

6.自定义对话框
自己做一个对话框my_dialog.xml,建一个类 继承系统的 DiaLlog.

class MyDialog extends Dialog{
     private TextView tvMsg;
     private String dialogName;
     private Button btnOK;//确认按钮
     private Button btnCancel;//取消按钮
                
     public MyDialog(Context context,String dialogName){
          super(context);
          this.DialogName=dialogName;
     }
     protected void onCreate(Bundle savedInstanceState){
          super.onCreate(savedInstanceState);
          requestWindFeature(Windos.FEATURE_NO_TITLE);//去处标题
          setContentView(R.layout_mydialog);//引入自定义的对话框布局文件
          tvMsg=(TextView)findViewByIdViewById(R.id.tv_msg);
          Button btnOK =(Button)findViewByIdViewById(R.id.tv_ok);
          Button btnCancel =(Button)findViewByIdViewById(R.id.tv_Cancel);
          vMsg.setText(dialogName);
          btnOK.setOnClickListener(new View.OnClickListener(){
               public void onClick(View v){
               }
          });
         btnCancel.setOnClickListener(new View.OnClickListener(){
               public void onClick(View v){
                   dismiss();
               }
         });
     }
}

调用:

MyDialog myDialog=new MyDialog(this,"我是要提示的字");
myDialog.show;

第四节 view其他细节


一、样式(一般作用在控件上)

在res-->values-->styles.xml中:

<style name="textStyle"><!--也可以继承(parent="")-->
    <item name="android:layout_width">match_parent</item>
</style>

使用的时候
style="@style/textStyle"

二、主题(一般作用在activity或者application上)

在res-->values-->styles.xml中
内容与样式相同
使用的时候: 在清单文件AndroidManifest.xml
<activity android:name=".MainActivity" android:theme="@style/textStyle">//加入该行

三、国际化(别名 i18n)

在res-->values-->创建string.xml文件、选择locale 选择相应国家然后编写属性(可以加多个国家)

四、单元测试

在项目创建之时,软件已经为我们创建了androidTest包与aoolication类,所有测试功能模块写入此类即可

五、LogCat的使用
六、debug调试

F8走到下一行 绿色播放键 走到下一个断点, F7走入方法内部

七、其他
android焦点的概念 就相当于windows中的光标
像你做登录程序的话,密码输入错误,焦点是不是要聚焦密码框好点,这样就不用鼠标去点密码框然后再输入密码,这样有利于用户体验。
比如在一个界面上你点击按钮,那么焦点就聚集在这个按钮上。
八、两种得到上下文的方法
1.因为activity继承了activity所以可以用this
2.直接用getApplicationContext()

第五节 Android中的动画


一.帧动画(Drawable Animation)【2.3不兼容(认为是耗时操作)】

就是加载一系列的图片资源,在drawable下建立一个xml文件[例:dome.xml]

<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true"> <!--是否执行一次 -->
      <item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
      <item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
      <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>

在显示页面

ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.drawable.dome);//设置背景
rocketAnimation = (AnimationDrawable) rocketImage.getBackground();//加载类型
rocketAnimation.start();//开启动画
二.view动画(也叫补间动画)
  • 【1】透明AlphaAnim
ImageView Image = (ImageView) findViewById(R.id.rocket_image);
//1表示完全不透明,0表示完全透明
AlphaAnimation aa = new AlphaAnimation(1.0f,0.0f)
aa.setDuration(2000);//执行时间
aa.setRepeatCount(1);//重复次数(不重复为0)
aa.setRepeatMode(Animation.REVERSE);//动画效果
Image.startAnimation(aa);
  • 【2】旋转rotateAnim
//RotateAnimation ra=new RotateAnimation(0,360)
RotateAnimation ra=new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,RELATIVE_TO_SELF,0.5f);
ra.setDuration(2000);//执行时间
ra.setRepeatCount(1);//重复次数(不重复为0)
ra.setRepeatMode(Animation.REVERSE);//动画效果
Image.startAnimation(ra);
  • 【3】缩放scaleAnim
ScaleAnimation sa=new ScaleAnimation(1f,2f,1f,2f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f)//放大(前四个参数)
sa.setDuration(2000);//执行时间
sa.setRepeatCount(1);//重复次数(不重复为0)
sa.setRepeatMode(Animation.REVERSE);//动画效果
Image.startAnimation(as);
  • 【4】位移translateAnim(不会改变真实位置)
  • 【5】一起用
    AnimationSet set =new AnimationSet(true)
    xml方式 定义补间动画
    res目录下创建anim文件夹,建立alpha文件
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
      android:fromAlpha="1.0f"
      android:toAlpha="0.0f"
      android:duration="3000"
      android:repeatCount="1"
      android:repeatMode="restart" />
Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim);
Image.startAnimation(animation);
三.属性动画(真实的改变控件)
ObjectAnimator oa=ObjectAnimator.ofFloat(iv."scaleY",0.1f,2,1,2);
oa.setDuration(2000);
oa.start();

第六节 Android中Activity


一个activity管理一个页面
setContentView(R.layout.factivity);决定加载哪个布局

一.activity的创建:

(五个状态、六个方法)
启动、运行、暂停、停止、销毁

onCreate()创建时调用、onStart()即将可见时调用、onResum()获取焦点与用户交互时调用
onPause()被覆盖或锁屏时调用、onStop()对用户不开见时调用、onRestart()从停止再启动时调用、onDestroy()销毁时调用
二.android的任务栈

一个应用对应一个任务栈(点开应用就会建立)
当页面启动时,页面对应的activity进栈。
按后退键,页面对应的activity就出栈
用户操作的页面永远是栈顶页面

三.activity的四种启动模式

在清单文件中。每一个activity都对应一个<activity>标签、其中的android:launchMode声明了启动方式

<activity android:name=".MainActivity" android:launchMode="standard">

默认为standard(即标准启动):每一次启动activity就在任务栈顶创建一个新的实例。
singleTop:创建检查任务栈栈顶,如果有实例就会复用(覆盖)该实例,不会创建新实例(浏览器书签)
singleTask:创建检查整个任务栈,如果栈内出现相同activity即复用该实例,并把该activity之上的实例全部清空(保证只有实例存在,浏览器页面)
singleInstance:会为实例单独创建任务栈(系统电话来电)

四.意图(Intent)

主要用于activity、server、发送广播
根据开启组件的方式不同,意图被分为两种(显式意图、隐式意图)

  • 显式意图:可以通过名称开启、指定目标组件
Intent intent=new Intent(this,MainActivity.class);
//参数:上下文+目标activity的字节码文件(指定具体类名)
startActivity(intent);
  • 隐式意图:通过指定action和category等属性,系统根据这些属性进行分析后寻找activity清单文件的意图过滤器:(action表示执行动作,category表示条件)
<intent-filter>
   <action android:name="cn.yijiajia.hh" />
   <category android:name="android.intent.category.LAUNCHER"/>(.LAUNCHER表示启动页面;.DELAULT表示普通)
</intent-filter>
Intent intent=new Intent();
intent.setAction("cn.yijiajia.hh");
intent.addCategory("android.intent.category.DELAULT");
startActivity(intent);

使用时间(显示意图更安全)
1.开启自己写的界面用显示意图
2.开启其他应用的界面(大多数指系统应用)用隐式意图


第七节 Activity操作


一、Activity之间的跳转
二、Activity之间数据传递

1.打开网页:

intent.setDate(Uri:parse("http://www.baidu.com"));
startActivity(intent);

2.传递信息:

Intent intent=new Intent(this,MainActivity.class);
intent.putExtra("date","你好");//背地里用map进行封装
startActivity(intent);
            
Intent intent=getIntent();
String date =intent.getStringExtra("date");//    String date data=intent.getStringExtra("date","默认");

3.数据的回传

  • 页面一
Intent intent=new Intent(this,Activity2.class);
startActivityForResult(intent,1);
//使用startActivityForResult方法开启一个新的activity,第一个参数是intent对象,第二个参数是请求码,用于判断来源
  • 页面二
Intent intent=new Intent();
intent.putExtra("date","你好");
setResult(1,intent);
  • 页面一
protected void onActivityResult(int requestCode,int resultCode,Intent date){
//本页面开启的页面关闭的时候执行(做数据回传的时候调用)[resultCode判断数据从哪来]
    super.onActivityResult(requestCode,resultCode,date);
    if(requestCode==1){
        if(resultCode==1){
            String string=data.getStringExtra("data")
        }
    }
}

4.关闭页面 finish();

四、Activity的启动模式
五、常见activity

1.拨打电话

Intent intent = new Inetent();
intent.setAction(Intent.ACTION_CALL);
intent.setDate(Uri.paese("tel:"+119));
startActivity(intent);

2.发短信

SmsManager smsManager=SmsManager.getDefault();
//1.一起发
smsManager.sendTextMessage("电话号码",null,"短信内容",null,null);
//2.分条发
String text="短信内容";
ArrayList<String> divide=smsManager.divideMessage(text);
for (String s:divide){
    smsManager.sendTextMessage("电话号码",null,s,null,null);
}

第八节 二种数据存储格式


一、XML解析
  • 1.DOM解析

将XML文件中所有内容以DOM数形式存放在内存中,支持删除、修改等功能,缺点是消耗内存大。

  • 2.SAX解析

逐行扫描XML文件,读取文件的同时即可以进行解析处理,不必等文件加载结束。缺点是无法进行增删改查

  • 3.PULL解析

一个开源的JAVA项目既可以用于AndRoid应用,也可以用于JavaEE程序。Android中已经继承了PULL解析器

  • 天气预报
<?xml version="1.0" encoding="utf-8"?>
    <infos>
        <city id="sh">
            <temp>20℃/30℃</temp>
            <weather>晴天多云</weather>
            <name>上海</name>
            <pm>80</pm>
            <wind>1级</wind>
        </city>
        <city id="bj">
            <temp>26℃/32℃</temp>
            <weather>晴天</weather>
            <name>北京</name>
            <pm>98</pm>
            <wind>3级</wind>
        </city>
    </infos>
</xml>
public static List<WeatherBean>getInfromXml(InputStream is)throws Exception{
    List<WeatherBean> weatherInfos=null;
    WeatherBean weatherBean=null;
    //获取pull解析器
    XmlPullParser parser=Xml.newPullParser();
    //告诉要解析的xml文件
    parser.setInput(is,"utf-8");
    //得到当前事件类型
    int type =parser.getEventType();
    //不到文件结尾 就一直解析
    while(type!=XmlPullParser.END_DOCUMENT){
        switch(type){
            case XmlPullParser.START_TAG ://解析的标签是开始标签
                if("infos".equals(parser.getName())){
                    weatherInfos =new ArrayList<WeatherBean>();
                }else if("city".equals(parser.getName())){
                    weatherBean=new weatherBean();  
                    String id= parser.getAttributeValue(0);
                    weatherBean.setId(id);
                }else if("temp".equals(parser.getName())){
                    String temp= parser.getAttributeValue(0);
                    weatherBean.settemp(temp)
                }
                break;
            case XmlPullParser.END_TAG :
                if("city".equals(parser.getName())){
                    weatherInfos.add(weatherBean);
                }
                    break;
        }
        type=parser.next();                
    }
    return weatherInfos;
}
二、JSON数据

JSON即JavaScript Object Notation (对象表示法),是一种轻量级的数据交换格式
JSON是基于纯文本的数据格式、可以传播String、Number、Boolean类型的数据,也可以传输数组,或者Object对象
JSON的扩展名为.json
JSON分为JSON对象和JSON数组两种数据结构

例子

{"name":"zhangsan" "address":{"city":"bejing" "postcode":100096}}
{"name":"zhangsan" "hobby":["羽毛球" "篮球"]}

JSON解析

{"name":"zhangsan","age":27,"married":true}
[16,2,26]
//解析对象
JSONObject jsonObj = new JSONObject(json1);
String name= jsonObj.optString("name");
int age =jsonObj.optInt("age");
//解析数组
JSONObject jsonObj = new JSONObject(json1);
for(int i=0;i<jsonArray.length();i++){
    int age =jsonArray.optInt(i);
}
//注意:optXXX()比getXXX方法更安全,不会抛出异常,得不到返回null或者0

2.用Gson库解析对象

//对象
Gson gson=new Gson();
Person person =gson.fromJson(json1,Person.class);
//数组
Gson gson=new Gson();
Type listType = new TypeToken<List<Integer>>(){}.getType();
List<Integer>ages =gson.fromJson(json2,listType);

················


第九节 Android中的五种数据存储方式


Android中有五种数据存储方式,它们分别是:
1.文件存储
2.SharedPreferences 存储简单配置信息(xml文件)
3.SQLiet数据库
4.ContentProvider(内容提供者) 可以将数据共享给其他应用
5.网络存储

一、文件存储的简介
  • 文件存储是Android中最基本的一种存储方式,它分成两种存储类型 内部存储、外部存储

内部存储写入

String fileName="data.txt";
String content="helloworld";
FileOutput fos;
try{
    fos =openFileOutput(fileName,MODE_PRIVATE);
    fos.write(content.getBytes());
    fos.close;
}catch(Exception e){
    e.printStackTrace();
}

外部存储

String state=Environment.getExternalStorageState();
if(stare.equals(Environment.MEDIA_MOUNTED)){//判断sd卡是否可用
    File SDPath=Environment.getExternalStorageStateDirectory();
    File file=new File(SDPath,"date.text");
    String date="HelloWorld";
    FileOutputStream fos;
    try{
        fos= new FileOutputStream(file);
        fos.write(data.getBytes());
        fos.close;
    }catch(Exception e){
        e.printStackTrace();
    }
  • QQ保存实战
//1.得到控件的值
String number=etText.getTex().toString().trim();//trim可以去除前后空格
//2.检查用户名密码是否为空
if(TextUtils.isEmpty(number)){}//为空返回true
//3.文件保存重新一个类
public class FileSaveQQ{
    //写入
    public static boolean save(Context context,String number,String password){
        try{
        //首先得到数据流
            FileOutputStream fos=content.openFileOutput("data.txt",content.MODE_PRIVATE);
            //把数据写到文件中
            fos.write((number+":"+password).getBytes());
            fos.close;
            return true;
        }catch(){}
    }
    //从文件中读取
    public static Map<String,String> get(Context context){
        try{
            //首先得到数据流
            FileOutputStream fis=content.openFileInput("data.txt");
            byte[] buffer=new byte[fis.available];
            fis.resd(buffer);
            Map<String,String>  userMap =new HashMap<>();
            String content=new String(buffer);
            String[] info=content.split(":");
            userMap.put("number",info[0]);
            userMap.put("password",info[1]);
            fis.close;
        }catch(){}
    }
}            
    //保存密码的数据回显
    Map<String,String> userInfo = FileSaveQQ.get(this);
    if(userInfo!=null){
        etNumber.setText(userInfo.get("number"));
        etpassword.setText(userInfo.get("password"));
    }
二、SharedPreferences

SharedPreferences是Android平台一个轻量级的存储类
用于存储配置参数、如:用户名、密码等
通过key/value(键值对)的形式将数据保存在Xml文件中
value的值,只能是float、int、long、boolean、String StringSet类型数据
使用方法
保存:

SharedPreferences sp=getSharedPreferences("data",MODE_PRIVATE);【保存成data.xml】
SharedPreferences.editor editor=sp.edit();
editor.putString("name","创智博客");
editor.putInt("age",8);
editor.commit();

获取:

SharedPreferences sp=getSharedPreferences("data",MODE_PRIVATE);
String date =sp.getString("name","");
三、SQLite数据库的操作与事务
  • 1.相关简介
    SQLiet是一个轻量级数据库,占用资源非常低。在内存中只需要占用几百KB的存储空间。
    SQLiet是遵循ACID的关系型数据库管理系统,ACID是指数据库事务正确执行的四个基本要素 (原子性、一致性、隔离性、持久性)
    SQLiet保存数据时,支持NULL(零)、INTEGER(整数)、REAL(浮点数字)、TEXT(字符串文本)、BLOB(二进制对象)五种类型

  • 2.创建数据库的方法
    只需要继承SQLiteOpenHelper即可

public class MyHelper extends SQLiteOpenHelper{
    public MyHelper (Context context){
        super(context,"itcast.db",null,2);//上下文、数据库文件名、游标工厂、版本
    }
    //第一次创建时候调用
    public void onCreate(SQLietDatebase db){
        db.execSQL("CREATE TABLE information(id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20),price INTEGER)");
    }
    //当实况版本号增加时候调用
    public void onUpgrade(SQLietDatebase db,int oldVersion,int newVersion){
    }
}

添加数据

public void intsert(String name,String price){
    SQLiteDatebase db=MyHelper.getWritableDatabase();
    ContentValues values= new ContentVables();
    values.put("name",name);
    values.put("price",price);
    long id =db.insert("information",null,values);//表名、sql语句不允许插入空行、表示values(固定类,底层用map实现)
    db.close();
}

修改数据

public int update(String name,String price){
    SQLiteDatebase db=MyHelper.getWritableDatabase();
    ContentVables values = new ContentValues();
    values.put("price",price);
    int number  =db.update("information",value,"name=?",new String[]{name});
    db.close();
    return number;
}

删除方法

public int delete(long id){
    SQLiteDatebase db= MyHelper.getWritableDatabas();
    int number =db.delete("information","id=?",new String[]{id+""});
    db.close;
    return number;
}

查询方法

//cursor是一个bean类
public boolean find(long id){
    SQLiteDatebase db=MyHelper.getReadableDatebase();
    Cursor cursor=db.query("information",null,"id=?",new String[]{id+""},null,null,null);
    boolean result = cursor.moreToNext();
    cursor.close();
    db.close();
    return result;
}

查询所有

public boolean find(long id){
    SQLiteDatebase db=MyHelper.getReadableDatebase();
    Cursor cursor=db.query("information",null,null,null,null,null);
    if(cursor.getCount()==0){
        //就是说没有数据
        Toast.makeText(this,"什么都没有",Toast.LENGTH_LONG).show;
    }else{
        cursor.moveToFrist();//把第一行数据取出来
        Toast.makeText(this,cursor.getString(1),Toast.LENGTH_LONG).show;
    }
    //当然你可以这样子把所有数据都拿出来
    while(cursor.moreToNext()){
        mTvShow.append("\n"+"name"+cursor.getString(1))
    }
    cursor.close;
    db.close;
}
  • 3.事务处理
PersonSQLiteOpenHelper helper= new PersonSQLiteOpenHelper(getContext());
SQLietDatebase db =helper.getWritableDatabas();
db.beginTransaction();
db.beginTransaction();
try{
    db.execSQL("update person set account =account -1000 where name = ?",new Object[]{"zhangsan"});
    db.execSQL("update person set account =account +1000 where name = ?",new Object[]{"lisi"});
    db.setTrabsactionSuccessful();
}catch(Exception E){
    Log.i("事务处理失败",e.toString);
}finally{
    db.endTransaction();
    db.close;
}

第十节 android中的Broadcast


一、Broadcast Receiver(广播接收者)
  • 1.广播接受者介绍:

1.Android系统中内置了很多广播(手机开发完成、电池电量过低都会发送)
2.为了监听来自系统或者程序的广播时间,Android通过了Broadcast Receiver组件
3.一个广播事件,可以有多个接收者处理

  • 2.BroadcastReceiver的使用

1.创建BroadcastReceiver对象
2.创建好以后记得注册
3.静态注册(既是以后不开应用,系统都会帮你创建广播)

<receiver
    android:name=".MyReceiver"
    android:enabled="true"
    android:exported="true">
</receiver>

动态注册(只有应用打开的时候才接受,注意需要注销)

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //实例化广播
    myReceiver= new MyReceiver();
    //实例化过滤器,并设置要接收的广播
    String action="android.provider.Telephony.SMS_RECEIVED";
    //创建意图过滤器对象
    IntentFilter intentFilter=new IntentFilter(action);
    registerReceiver(myReceiver,intentFilter);
}

protected void onDestroy() {
    super.onDestroy();
    //注销
    unregisterReceiver(myReceiver);
}

练习题.电话拦截器——保存电话:

SharedPreferences sp=getSharedPreferences("data",MODE_PRIVATE);
SharedPreferences.Editor editor=sp.edit();
editor.putString("name","你好");
editor.commit();
获取:
SharedPreferences sp=getSharedPreferences("data",MODE_PRIVATE);
String date =sp.getString("name","");
新的API
getResultDate();//得到电话
setResultDate(null);//修改电话

修改电话,需要在清单文件中声明

<receiver
    <intent-filter>
        <action android:name="android.intent.action.NEW_OUTGOING_CALL"></action>
    </intent-filter>
</receiver>

修改电话,涉及到打电话权限
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

二、自定义广播

1.当自定义广播发送消息时,会存储到公共消息区中,而公共消息区如果存在对应的广播接受者,就会及时接受到这条消息

2.拯救史迪仔

Intent intent=new Intent();
//定义广播的事件类型
intent.setAction("help_Stitch");
//发送广播
sendBroadcast(intent)

在控制台输出代码
Log.i("BroadcastReceiver","我收到了");
当然接受者要写相应的拦截器

<intent-filter>
    <action android:name="help_Stitch"></action>
</intent-filter>
三、广播的类型

1.无序广播:完全异步执行,发送广播时所有监听该广播的接收者会同时收到消息

    Intent intent=new Intent();
    intent.setAction("help_Stitch");
    intent.putExtra("data","鬼子进村了");
    sendBroadcast(intent);

2.有序广播:安装接收者的优先级,只有一个广播接收后,执行完相应逻辑,才会继续传播。(可以被拦截)
优先级设定:

<receiver>
    <intent-filter 
        android:name=""
        android:priority="" //值越大,优先级越高(相同级别,先创建先收到)
    >
</receiver>

发送广播

Intent intent=new Intent();
//定义广播的事件类型
intent.setAction("help_Stitch");
//发送广播
sendOrderedBroadcast(intent,null);//第二个是权限,不需要则设置为null

接收广播

  • 拦截
    abortBroadcast();
  • 修改
    setResultData("上面发了500斤大米");
  • 得到数据
    getResultData()

3.写入一定会被某个接收者的广播

sendOrderedBroadcast(intent,null,new final(),null,0,"发1000斤大米",null);
//myReceiver即使等级很低,即使被拦截,依然会被final接收(final不需要配置)

四、广播打开activity

Intent intent=new Intent(this,Activity2.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
content.startActivity(intent);

第十一节 android中的server


startService一旦开启就要手动关闭,而bingService()与Actity组件绑定 不求同年同月同日但求同年同月同日死

一、服务的创建
  • 1.服务可以看做是一个没有界面的Activity。比如音乐的播放

特点:在后台长期运行

  • 2.服务的创建
二、服务的声明周期
  • 1.和activity一样创建与销毁
  • 2.服务的开启方式有两种

(1)通过startService()方式启动

通过startService()方式启动,需要自身调用stopSelf()方法或其他组件调用stopService()方法服务才能停止
onCreate()-》onStartCommand()-》Service running-》上面-》onDestroy()

(2)通过bindService()方式启动

通过bindService()方式启动,需要调用onUnbind方法解除绑定才能销毁
onCreate()-》onBind()-》Service running-》上面-》onDestroy()
三、服务的启动方式

案例1.
通过startService:特点:与开启者没有关系。既是启动组件被销毁,服务依然运行。
onStartCommand()可以执行多次
需要在Service中写onCreate()、onStartCommand()、onDestroy()方法; onBind()不用,返回null

//开启:
Intent intent =new Intent(this,MyServer.class);
startService(intent);
//关闭:
Intent intent =new Intent(this,MyServer.class);
stopService(intent);

案例2.
通过bingService()
需要在Service中写onCreate()、onUnbind()方法;||onBind()方法绑定(返回一个IBinder数据类型)、onUnbind()解绑 onBind()不能多次执行
需一个内部类继承Binder(已经实现了IBinder接口)
//通过该对象可以间接的访问服务(在里面可以调用该服务的方法)
class MyBinder extends Binder{
}
//IBinder是夸进程访问

开启:
bindServer(intent,conn,flags);||//意图(指明要开启哪一个),ServiceConnection用于监听调用者与service的连接状态,指明连接时是否自动创建service
成员变量MyConn myconn;
在某个方法内
{
    if(myconn==null){
        myconn=new MyConn();
    }
    Intent intent =new Intent(this,MyServer.class);
    bindServer(intent,myconn,BIND_ABOVE_CLIENT);
}
//在Activity中创建一个内部类
private class MyConn implements ServiceConnection{
//当服务连接的时候,调用该方法,
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    MyService.MyBinder myBinder=(MyService.MyBinder)iBinder;
    log.i("MainActivity","服务成功绑定")
}
//当服务失去连接的时候调用该方法
public void onServiceDisconnected(ComponentName componentName) {
}
         
//关闭:用onUnbind
if(myconn!=null){
    unbindService(myconn);
    myconn==null
}
四、通信方式

本地通信(运行在自己应用的服务)
远程服务通信(运行在其他应用的服务)【AIDL】
android interface Definition language;

  • 1.先把interface 文件后缀改成aidl[Stub使用进程间通信IPC]
  • 2.服务类中间人mybind直接继承stub
  • 3.在本地创建一个一模一样的包 一模一样的.aidl文件
  • 4.在获取的时候不使用iservice=(Iservice)iBinder;而是 iservice=stub.asInterface(server);
五、音乐播放器
MediaPlayer mediaPlayer=new MediaPlayer();
//指定音频文件
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource("dizhi");
mediaPlayer.prepare();//准备播
mediaPlayer.start();//开始播
if(mediaPlayer.isplaying())//判断在不在播放
mediaPlayer.stop;
mediaPlayer.release();
mediaPlayer=null;
六、进程概念【一个进程含有多个线程】

四大组件都运行在主线程中(换句话说 四大组件都运行在主线程中)
系统尽可能长时间的维持进程的操作,
五种进程的优先级

  • 1.前台进程(正在调用onResum()方法)
  • 2.可视进程(用户可见,影响用户看得见)
  • 3.服务进程(通过通过startService()方式启动)【一般不会杀到这里】
  • 4.后台进程(activity进行了onstop()方法)
  • 5.空进程(不维持任何激活组件)[有时不杀死,提高速度]
七、通过特定的接口 暴露想暴露的方法
  • 1.定义一个接口 把自己想暴露的方法暴露在接口中
  • 2.定义的中间人实现接口
  • 3.在获取中间人对象时mybinder
八、混合方式开启服务(既想长期运行 又想调用里面的方法)

startserver让服务运行
在服务类中创建中间人mybind继承Binder
mybind方法实现接口类
服务类onBind方法返回刚才创建的类
在actity中创建myconn实现ServiceConnection类 然后new出来
然后bindService(intent,myconn,BIND_AUTO_CREATE);
调用unbindserver解绑服务


第十二节 android中的内容提供者contentProvider


一、内容提供者(contentProvider)

内容提供者是android的四大组件之一,它是不同应用程序之间进行数据共享的标准API
ContentResolver类可以访问ContentPrivider中共享数据
他们主要是通过ContentProvider 与ContentResolver两个组件

  • 1.ContentResolver提供了一系列的增删改查方法对数据进行操作,并将这些方法以uri的形式对外通过数据
  • 2.uri为内容提供者中的数据建立了唯一标识符。主要由3部分构成
1.scheme部分:content://是标准前缀,不能被修改
2.authority部分:创建内容提供者指定的authorities属性值,通常采用程序包名的方式命名
3.path部分:/person 代表资源或数据,可以动态改变

例:content://cn.itcast.mycontentprovider/person

二、内容提供者的创建{在日志中搜索Pub}

创建:
new->Other->Content Provider
URI Authorities 通过哪一个uri可以访问该内容提供者 (一般用包名 cn.itcast.mycontentprovider)
Exported:是否暴露给别人
Enabled:是否允许系统自动创建
其中几个方法
getType:获取媒体类型
需要在清单文件中声明
例子:(这里以查询为例)

public class MyContentProvider extends ContentProvider {
    private static final UriMatcher sUriMatcher=new UriMatcher(UriMatcher.NO_MATCH);//不匹配返回-1
    private static final int QUERYSUCCSEE=0;
    private static final int INSERTSUCCSEE=1;
    private MyHelper myHelper;
    public MyContentProvider() {
        sUriMatcher.addURI("henan.com.provider","query",QUERYSUCCSEE);
        sUriMatcher.addURI("henan.com.provider","insert",INSERTSUCCSEE);
    }
    
    @Override
    public boolean onCreate() {
        myHelper = new MyHelper(getContext());
        return false;
    }
    
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        throw new UnsupportedOperationException("Not yet implemented");
    }
    
    @Override
    public String getType(Uri uri) {
        throw new UnsupportedOperationException("Not yet implemented");
    }
    
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        int code = sUriMatcher.match(uri);
        if (code==INSERTSUCCSEE){
            SQLiteDatabase myDatabase = myHelper.getReadableDatabase();
            long insert=myDatabase.insert("info",null,values);
            Uri uri1=Uri.parse("com.henan.insert/"+insert);
            return uri1;
        }else {
            return null;
        }
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        int code = sUriMatcher.match(uri);
        if (code==QUERYSUCCSEE){
            SQLiteDatabase myDatabase = myHelper.getReadableDatabase();
            Cursor query = myDatabase.query("info", projection, selection, selectionArgs, null, null, sortOrder);
            myDatabase.close()
            //这里cursor不用关,但是数据库要关
            return query;
        }else {
            return null;
        }
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        throw new UnsupportedOperationException("Not yet implemented");
    }
}
三、内容解析者

查询

Uri uri = Uri.parse("content://henan.com.provider/query");
ContentResolver contentResolver= getContentResolver();//获取ContentResolver对象
Cursor cursor=contentResolver.query(uri,new String[]{"name","age"},null,null,null);//条件、条件参数、排序
while (cursor.moveToNext()){
    String name=cursor.getString(1);
    int age=cursor.getInt(2);
}
cursor.close();

插入

Uri uri = Uri.parse("content://henan.com.provider/insert");
ContentValues contentValues=new ContentValues();
contentValues.put("name","张三");
contentValues.put("password","123");
ContentResolver contentResolver= getContentResolver();//获取ContentResolver对象
Uri uri=contentResolver.insert(uri,contentValues);
四、查看短信案例
五、内容观察者ContentObserver

1.内容观察者是用来指定Uri所代表的数据。当ContentObserver观察到指定Uri代表的数据发生变化时,就会触发onChange()方法,在该方法中使用ContentProvider可查询数据变化
2.要使用ContentObserver观察数据变化,就必须在ContentProvider的delete()、insert()、update()方法中调用ContentResover的notifyChange()方法;
假设B程序调用A程序


/*
 *A程序:ContentProvider暴露数据并调用ContentResolver的notifyChange()方法
 *      当程序发生变化是,A向信息中心发生消息
 *B程序:使用ContentResolver操作A程序的数据
 *C程序:注册ContentObserver
 *     观察消息中心的消息,通过消息观察A程序的数据变化
 *     当观察到变化的数据触发onChange()方法
 */

//ContentObserver的两个常用方法:
public ContentObserver(Handler handler){}
//ContentObserver的派生类都需要调用该构造方法,参数可以是主线程Handler,也可以是其他handler对象
public void onChange(boolean selfChange){}
//当观察者Uri代表的数据发生变化时,会触发该方法。在该方法中使用ContentResovler可以查询到变化数据
Uri uri = Uri.parse("content://cn.itcast.mycontentprovider/person");//一定要注意首字母大写其余小写
ContentResolver contentResolver= getContentResolver();//获取ContentResolver对象
contentResolver.registerContentObserver(uri,true,MyObserver(new Handler()));//第二个参数:是否精确匹配uri true只匹配该uri false匹配其派生类 第三个参数:ContentObserver实例 
六、常用的几个系统表

data表:datal存所有人联系的信息 mimetype_id区分是什么信息 raw_contact_id:存放有多少条信息
raw_contacts:mimetype表


第十三节 初步http协议


一、http协议:

超文本传输协议

二、HttpURLConnection的基本用法
URL url=new URL("http://www.itcast.cn");
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);//连接超时时间
InputStream is=conn.getInputStream();
conn.disconnect();
三、GET和POST请求方式
String path = "http://xx.xxx.xx.xxx:80/web/LoginServlet?username="+URLEncoder.encode("zhangsan")+"&password="+URLEncoder.encode("123");
URL url=new URL(path);
HttpURLConnection conn=(HttpURLConnection)url.openConnection();

第十四节 多线程初步


一、多线程编程

android的主线程不能进行耗时操作(4.0以后强制,主线程也没这个义务)
打印线程名称方法:
Thread.currentThread().getName();(Android的主线程就是UI线程,只有UI线程才可以更新ui)

开启多线程的方法一:

第一步:new Thread(){}
第二步:new Thread(){}.stare()
第三步:new Thread(){public void run(){};}.stare()

开启多线程的方法二:(基本上用第一种,第二种是在特殊方法中使用)

new Runnable(){}
new Runnable(){public void run(){};}
二、handle机制

主线程 含handler,可以告诉系统更新ui
handler的作用是发消息和处理消息
looper是从消息队列里取消息
(该模型十分重要)

1.以下是更新ui的方法(该方法小白使用,存在内存泄漏风险)

Handler handler=new Handler(){
//该方法在UI线程执行
    @Override
    public void handleMessage(Message msg) {
        String s=(String)msg.obj;
        view.setText(s);
    }
}

new Thread(){
    @Override
    public void run() {
        Message msg=new Message();
        String content="内容";
        msg.obj=content;
        handler.sendMessage(msg);//发送一条消息,数据放到msg里(子线程)handleMessage方法便会执行
    }
}.start();

图片查看器

new Thread(){
    try{
        //获取访问图片路径
        String path=et_view.getText().toString().trim();
        //创建URL路径
        URL url=new URL(path);
        //获取httpurlconnection
        HttpURLConnection  conn=(HttpURLConnection)url.openConnection();
        //设置请求方式
        conn.setRequestMethod("GET");
        //设置超时时间
        conn.setConnectTimeout(5000)
        //获取服务器返回状态码
        int code=conn.getResponseCode();
        if(code == 200){//200代表成功 404代表没有找到
            //获取图片数据(基本上都是以流的形式接收)
            InputStream in=conn.getInputStream();
            //通过位图工厂获取位图
            Bitmap bitmap=BitmapFactory.decodeStream(in);
            //把位图显示到iv上
            Message msg= Message.obtain();//使用msg的静态方法 可以减少对对象的创建
            msg.obj=bitmap;
            handler.sendMessage(msg);
         }
    }
}.start();
三、缓存机制
//缓存图片,谷歌给我们提供了一个缓存目录getCacheDir() //创建缓存目录[类似于 getFilesDir()//创建file目录]
File file=new File(getCacheDir(),"Beautiful.png")
if(file.exists()&&file.length()>0){
    Bitmap cacheBitmap=BitmapFactory.decodeFile(file.getAbsolutePath());//
    Message msg=Message.obtain();
    msg.obj=cacheBitmap;
    handler.sendMessage(msg)
}else{
    //第一次
}
四、cache目录和filedir目录区别

cache可以被用户手动清除掉缓存(用户可以清楚)
file可以被用户手动清除掉data

五、runOnUiThread();

作用:在主线程中会立即执行 在子线程中会交给主线程执行

runOnUiThread(new Runnable(){
    @Override
    public void run() {
        view.setText("xxx");
    }
});
六、常见的API

下面的方法不能更新ui(当然可以用runOnUiThread())【大部分在企业中是没办法用的,有兴趣的同学可以学习线程池】
第一个:handler

//2秒后执行run方法
new Handler().postDelayed(new Runnable(){
    public void run(){
        syso("哈哈哈")
    }
},2000)

第二个:time

//5秒后进行run方法
Timer timer =new Timer();
TimerTask task=new TimerTask(){
   public void run(){
       syso("呵呵呵")
   }
};
timer.schedule(task,5000)[也可以是timer.schedule(task,5000,1000)]
     
//销毁方法
timer.cancel();
task.cancel();
七、将字符流转换成string的方法
public String inputStream2String(InputStream is)   throws IOException{
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    StringBuilder sb = new StringBuilder();
    String line = null;
    try {
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return sb.toString();
}
八、服务器的逻辑
public void androidlogin() {
/*使用servletActionContext对象拿request与Response都很简单*/
HttpServletRequest request=ServletActionContext.getRequest();
String phone=request.getParameter("phone");
String password=request.getParameter("password");
System.out.println(phone);
HttpServletResponse response=ServletActionContext.getResponse();
response.setContentType("text/plain; charset=utf-8");
PrintWriter out;
try {
    out = response.getWriter();
    out.print("登录成功");
    out.flush();
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
九、HttpURLConnection的get与post客户端逻辑
public void click1(View view) {
    String name=name_edit.getText().toString().trim();
    String password=password_edit.getText().toString().trim();
    final String path="http://117.34.240.61:8080/AndroidServiceDome2/user_androidlogin.action?phone="+name+"&password="+password;
    new Thread(){
        @Override
        public void run() {
            try{
                URL url=new URL(path);
                HttpURLConnection conn=(HttpURLConnection)url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                int code=conn.getResponseCode();
                if(code == 200) {//200代表成功 404代表没有找到
                    InputStream inputStream = conn.getInputStream();
                    Util util=new Util();
                    String S =util.inputStream2String(inputStream);
                    //因為不可以在子線程展示ui所以
                    showToast(S);
                }
            }catch (Exception e){
            }
        }
    }.start();
}
public void click2(View view) {
    final String name=name_edit.getText().toString().trim();
    final String password=password_edit.getText().toString().trim();
    final String path="http://117.34.240.61:8080/AndroidServiceDome2/user_androidlogin.action";
    new Thread(){
        @Override
        public void run() {
            try{
                String data="phone="+name+"&password="+password;
                URL url=new URL(path);
                HttpURLConnection conn=(HttpURLConnection)url.openConnection();
                conn.setRequestMethod("POST");
                conn.setConnectTimeout(5000);
                conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
                conn.setRequestProperty("Content-Length",data.length()+"");
                conn.setDoOutput(true);
                conn.getOutputStream().write(data.getBytes());
                int code=conn.getResponseCode();
                if(code == 200) {//200代表成功 404代表没有找到
                    InputStream inputStream = conn.getInputStream();
                    Util util=new Util();
                    String S =util.inputStream2String(inputStream);
                    //因為不可以在子線程展示ui所以
                    showToast(S);
                }
            }catch (Exception e){
            }
        }
    }.start();
}
十、httpclient(使用直接子类Defaulthttpcliet)【该方法已经过时】
DefaultHttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(path);
HttpResponse response = client.execute(get);
int code = response.getStatusLine().getStatusCode();
if(code == 200){
    InputStream inputStream=response.getEntity().getContent();
    showToast(content);
}
DefaultHttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(path);
List<NameValuePair> lists= new ArrayList<NameValuePair>();
BasicNameValuePair nameValuePair = new BasicNameValuePair("phone",name);
BasicNameValuePair passwordValuePair = new BasicNameValuePair("password",password);
lists.add(nameValuePair);
lists.add(passwordValuePair);
URLEncodedFormEntity entity = new URLEncodedFormEntity(lists);
post.setEntity(entity);
HttpResponse response = client.execute(post);
int code = response.getStatusLine().getStatusCode();
if(code == 200){
    InputStream inputStream=response.getEntity().getContent();
    showToast(content);
}
十一、retrofit

下载包

//retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0' 
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
十二、多线程加速下载

1.注意

  • 不是线程越多下载越快
  • 下载速度受服务器带宽的影响

2.多线程下载步骤
[1]获取文件大小

int length=conn.getContentLength()//得到字节长度
runningThread = threadCount;

[2]在客户端创建一个大小和服务器一模一样的文件提前申请好空间

RandomAccessFile rafAccessFile = new RandomAccessFile("dome.exe","rw");
rafAccessFile.setLength(length);
//[2.1]确定每个线程下载的开始位置与结束位置
int blockSize =length/threadCount;//提前声明好有几个线程
for(int i=0; i<theadCount;i++){
    int startIndex = i*blockSize;
    int endIndex=(i+1)*blockSize-1;
    if(i==threadCount-1){
        endIndex=length-1;
        syso("线程"+i+"理论下载的位置"+startIndex+"-----"+endIndex)
        //开启线程
        DownLoadThread  downLoadThread = new DownLoadThread(startIndex,endIndex,i);
        downLoadThread.start();
     }
}

[3]开多个线程去下载文件

//这里单独抽取一下
private static class DownLoadThread extends Thread{
    private int startIndex;
    private int endIndex;
    private int threadId;
    
    public DownLoadThread (int startIndex,int endIndex,int threadId){
        this.startIndex=startIndex;
        this.endIndex=endIndex;
        this.threadId=threadId;
    }
    
    public void run(){
        try{
            URL url=new URL(path);
            HttpURLConnection conn=(HttpURLConnection)url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
                            
            //再次下载要先看看上一次是不是没下完、
            File file =new File(threadId+".txt")
            if(file.exists()&&file.length()>0){
                FileInputStream fis =new FileInputStream(file);
                BufferedReader bufr =new BufferedReader(new InputStreamReader(fis));
                String lastPositionn=bufr.readLine();
                int lastPosition=Integer.parseInt(lastPositionn);
                startIndex=lastPosition;
                syso("线程"+threadId+"真实下载的位置"+startIndex+"-----"+endIndex)
                fis.close;
             }
             //[3.1]设置一个请求头Range
             conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);
             int code=conn.getResponseCode();
             if(code == 200) {//200代表成功 404代表没有找到
                //创建随机文件读写对象
                RandomAccessFile raf = new RandomAccessFile("dome.exe","rw");
                ref.seek(startIndex);
                InputStream inputStream = conn.getInputStream();
                //把内容写到文件中
                int len=-1;
                /*****断点续传(把当前进度用文件存起来)****/
                int total=0;//代表当前下载的大小
                byte[] buffer=new byte[2014*1024];//定义缓冲区的大小
                while((len = in.read(buffer))!=-1){
                    raf.write(buffer,0,len);
                    total +=len;
                    int currentThreadPosition=startIndex+total;
                    /*该方式不安全*/
                    //File file = new File(threadId+".txt")
                    //FileOutputStream fos = new FileOutputStream(file);
                    //fos.write(String.valueOf(currentThreadPosition).getBytes());
                    //fos.close;
                    RandomAccessFile raff =new RandomAccessFile(threadId+".txt","rws");raff.write(String.valueOf(currentThreadPosition).getBytes());
                    raff.close;
                }
            raf.close();
            syso("线程id:"+threadId+"加载完毕")
            //把txt文件删除(之前声明一个runningThread 代表当前正在运行的线程)
            synchronized(DownLoadThread.class){//线程锁
                runningThread--;
                if(runningThread==0){
                //说明执行完毕
                    for(int i=0;i<threadCount;i++){
                        File deletFile=new File(i+".txt");
                        delteFile.delete();
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }    
    }
    public static String getFilenam(String path){
        int start =path.lastIndexOf("/")+1;
        return path.substring(start);
    }
}

[4]知道每个线程什么时候下载完毕将文件拼接起来即可


第十五节 多媒体初步


一、计算机表示图形的几种方式

bmp:以高质量保存
jpg:以良好质量保存
png:以高质量保存(png与jpg差不多 只不过压缩算法不一样,png压缩算法更好)
图片大小的计算公式:(图片总像素 X 每一个像素的大小)
单色: 只有两种颜色(要么是黑要么是白) 每一个像素只要长度是1的二进制位表示(一个像素占1/8个byte)
16色: 每个像素最多可以表示16种颜色 0000-1111 只需要使用长度为4的二进制表示(一个像素占1/2个byte)
256色: 每个像素最多可以表示256种颜色 0000 0000-1111 1111 只需要使用长度为8的二进制表示(一个像素占1个byte)
24位: 每个像素最多可以表示1600万种颜色 RGB组合 一个像素占3byte(RGB各占一个byte)
在android中采用png,采用的是arpg一个像素四个byte

二、缩放加载大图片
Bitmap bitmap=BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg")
imageView.setImageBitmap()

(oom内存溢出异常,因为默认给每个应用就分配16m空间,而图片加载申请内存与图片大小有关 与真实大小无关)

正确方法:

  • 1.获取图片分辨
  • 2.获取手机分辨率
  • 3.计算缩放比(宽除以宽,高除以高,然后按照大的缩放)
//获取手机宽与高
WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);
//第一种过时
int height = wm.getDefaultDisplay().getHeight();
int width = wm.getDefaultDisplay().getWidth();
//第二种
Point point = new Point();
wm.getDefaultDisplay().getSize(point);
int height1 = point.x;
int width1 = point.y;
//加载图片信息
//创建一个位图工厂创建一个参数
BitmapFactory.Options options=new BitmapFactory.Options();
//解码器不去真正的解析位图 但是还能够获取图片的宽与高信息
options.inJustDecodeBounds =true;
BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg",options);
int imgw=options.outWidth;
int imgh=options.outHeight;
//计算缩放比
int scale=(imgw/height)>=(imgh/height)?(imgw/height):(imgh/height);
//但是不能小于1
if (scale<1){
        scale=1;
}
options.inSampleSize=scale;
//加载图片
options.inJustDecodeBounds =false;
Bitmap bitmap = BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg", options);
                imageView.setImageBitmap(bitmap);
三、创建原图副本
//把普通照片转化成bitmap
Bitmap srcBitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background);
//创建了一个与原图一样的空白的白纸
Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
//用画笔画一个与原图一样的图
//1.拿画笔
Paint paint=new Paint();
//2.拿画布并把把画布铺在copy图上
Canvas canvas=new Canvas(copyBitmap);
//3.画图
canvas.drawBitmap(srcBitmap,new Matrix(),paint);
//已经画好的图修改(一次修改一个像素)
copyBitmap.setPixel(20,20, Color.BLUE);
四、图形处理的api

旋转

//1.拿画笔
Paint paint=new Paint();
//2.拿画布并把把画布铺在copy图上
Canvas canvas=new Canvas(copyBitmap);
Matrix matrix = new Matrix();
//旋转角度,基于什么中心点           matrix.setRotate(20,copyBitmap.getWidth()/2,copyBitmap.getHeight()/2);
canvas.drawBitmap(srcBitmap,matrix,paint);

缩放

//缩放比例(水平,竖直)
matrix.setScale(0.5f,0.4f);

平移

//水平,竖直
matrix.setTranslate(30,0);

镜面(旋转平移组合)

matrix.setScale(-1f,0);
//使用post是在修改的基础上修改
matrix.postTranslate(copyBitmap.getWidth(),0);

倒影

matrix.setScale(1f,-1f);
matrix.postTranslate(0,copyBitmap.getHeight());
五、使用mediaplayer播放音频文件(mediaPlayer只能播放mp4或者3gp格式)
MediaPlayer mediaPlayer=new MediaPlayer();
try {
    mediaPlayer.setDataSource("/mnt/sdcard/dog.mp3");
    mediaPlayer.prepare();
    mediaPlayer.start();
} catch (IOException e) {
    e.printStackTrace();
}
mediaPlayer.stop();
六、surfaceview介绍(控件)

他是一个重量级控件
维护两个线程A、加载数据 B、显示数据 可以直接更新ui

View scroll = findViewById(R.id.scrollView1);
SurfaceHolder handler = (SurfaceHolder)scroll.getHandler();
MediaPlayer mediaPlayer = new MediaPlayer();
try {
    mediaPlayer.setDataSource("/mnt/sdcard/dog.mp4");
    mediaPlayer.prepareAsync();
    mediaPlayer.setDisplay(handler);
    mediaPlayer.start();
} catch (IOException e) {
    e.printStackTrace();
}
七、VideoView控件介绍
VideoView videoView = findViewById(R.id.video_view);
videoView.setVideoPath("/mnt/sdcard/dog.mp4");
videoView.start();
八、vitamio框架
九、照相与录像

第十六节 Fragment入门


一、fragment入门 [最低11版本(即3.0)]

它总是被嵌入到activity当中,生命周期被activity影响

  • 1.ViewGroup可以有自己的孩子
  • 2.view 没有自己的孩子

通过 onCreateView这个方法可以加载fragmen自己的布局
第一种加载fragment方式

<fragment
    android:layout_width="0dp"
    android:layout_weight="1"
    android:layout_height="match_parent"
    android:id="@+id/addfragment_first"
    android:name="dome813.henan.com.fragmentdome.Fragment1"
/>
<fragment
    android:layout_width="0dp"
    android:layout_weight="1"
    android:layout_height="match_parent"
    android:id="@+id/addfragment_second"
    android:name="dome813.henan.com.fragmentdome.Fragment2"
/>
public class Fragment2 extends Fragment {
    //第一次调UI的时候执行该方法 加载fragment自己的控件
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
    //通过打气筒将布局转化成view
        View view = inflater.inflate(R.layout.fragmentlayout2,null);
        return view;
    }
}

第二种加载fragment方式(动态加载)

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
String state = null;
if (state=="横屏"){
    fragmentTransaction.replace(android.R.id.content,new Fragment1());
}else{
    fragmentTransaction.replace(android.R.id.content,new Fragment2());
}
fragmentTransaction.commit();

3.fragment的xml中 不能用onClick方法点击,只能通过id方式 setOnClickListener
4.兼容11版本以下的 就要使V4包

二、fragment的生命周期
onAttach()
    onCreate()
        onCreateView()//第一次画ui 必须重新
            onActivityCreated()//准备view 已初始化
                onStart()
                    onResum()
                    onPause()
                onStop()
        onDestroyView()
    onDestroy()//回收内存
onDetach
三、fragment之间的通信 (通过activity)
public class Fragment1 extends Fragment {
    //第一次调UI的时候执行该方法 加载fragment自己的控件
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
    //通过打气筒将布局转化成view
        View view = inflater.inflate(R.layout.fragmentlayout1,null);
        view.findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Fragment2 fragment2 =(Fragment2)getActivity().getFragmentManager().findFragmentByTag("massage1");
         }
    });
    return view;
    }
}
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    .....
        if (state=="横屏"){
            fragmentTransaction.replace(android.R.id.content,new Fragment1(),"message1");
        else{
            fragmentTransaction.replace(android.R.id.content,new Fragment2(),"message2");
        }
        fragmentTransaction.commit();
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,783评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,396评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,834评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,036评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,035评论 5 362
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,242评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,727评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,376评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,508评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,415评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,463评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,140评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,734评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,809评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,028评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,521评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,119评论 2 341

推荐阅读更多精彩内容