chap5-多线程与网络应用_第1页
chap5-多线程与网络应用_第2页
chap5-多线程与网络应用_第3页
chap5-多线程与网络应用_第4页
chap5-多线程与网络应用_第5页
已阅读5页,还剩52页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

多线程与网络应用

5.1任务1-使用多线程与Handler21多线程的编程方法2Handler与后台线程的交互3消息与Bundle后台线程Thread耗时的数据获取与处理(例如网络连接、网络数据获取以及较复杂的数据计算等)交给后台Thread(线程)或者Service(服务)Activity或者Fragment等UI线程中则负责接收处理后的数据,渲染更新UIAndroid的后台线程以及Service不允许直接更新UIThread的使用改写run()方法对Thread对象调用start()方法启动线程run()方法运行结束,线程消亡在主UI线程中调用后台线程对象的run()方法,能执行对应的代码,但是并不是在后台线程中执行,而是在UI线程中直接执行后台线程的run()方法,后台线程角色失效前后台多线程之间的数据交互方法通过Handler(句柄)实现事件传递和数据交互通过自定义接口,在后台线程中切换主UI线程,并产生接口回调,在主UI线程中实现接口并响应回调通过Android的LiveData,利用生产者和观察者模式,在后台线程中对LiveData数据通过postValue()方法修改值,在主UI线程中则对LiveData调用Observer接口侦听数据变动3句柄Handler4前端定义Handler对象,并处理handleMessage()回调从消息中获得数据,用于更新UI。后台线程通过Handler对象获得Message对象,将数据装入Message,并通过Handler发送消息,两者配合实现了后台线程与前端UI的数据交互。实现UI布局my_main.xml5<LinearLayoutxmlns:android="/apk/res/android"

xmlns:app="/apk/res-auto"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent">

<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="YournameandID"/>

<TextView

android:id="@+id/tv_result"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="0.00"/>

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal">

<Button

android:id="@+id/bt_start"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:text="Start"/>

<Button

android:id="@+id/bt_stop"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:enabled="false"

android:text="Stop"/>

<!--通过设置android:enabled属性可控制Button是否可用-->

</LinearLayout>

</LinearLayout>实现自定义线程CounterThread6publicclassCounterThreadextendsThread{

privatestaticfinalStringKEY_COUNTER="key_counter";

privateAtomicBooleanisRunning;//原子变量,使之多线程操作安全

privateHandlerhandler;//handler由外部传入,在UI线程中定义handler

//注意导入包是android.os.Handler,而非java.util.logging.Handler

publicCounterThread(Handlerhandler){

this.handler=handler;

}

@Override

publicvoidrun(){

isRunning=newAtomicBoolean(true);

floatcounter=0.0f;

while(isRunning.get()){

try{

Thread.sleep(10);//睡眠10ms,可能会有异常,用try-catch捕捉

//非精准计时,与实际时间有误差

}catch(InterruptedExceptione){

e.printStackTrace();

}

counter+=0.01f;

Bundlebundle=newBundle();

//Handler对象利用Bundle打包数据

//Bundle可理解为一个超级HashMap,通过key-value存放数据

//Bundle支持常用数据存取的指定方法,避免强制类型转换

bundle.putFloat(KEY_COUNTER,counter);

Messagemsg=handler.obtainMessage();//从Handler对象中取出消息对象

msg.setData(bundle);//将Bundle对象放入消息对象

handler.sendMessage(msg);//将消息通过Handler对象发送出去

//句柄对象Handler在主UI中回调handleMessage()处理消息,并更新UI

}

}

publicvoidstopCounter(){

isRunning.set(false);

//isRunning设置为false,使得run()方法中while条件不再成立,循环结束

}

publicstaticfloatgetMessageData(Messagemsg){

//定义为static方法,使之不依赖于实例对象

//解析消息数据直接在该类中实现有利于解耦,外部无需关心Bundle对象存放数据的key

Bundlebundle=msg.getData();

floatv=bundle.getFloat(KEY_COUNTER);

returnv;

}

}实现MainActivity7publicclassMainActivityextendsAppCompatActivity{

Handlerhandler;

CounterThreadthread;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.my_main);

TextViewtv=findViewById(R.id.tv_result);

Buttonbt_start=findViewById(R.id.bt_start);

Buttonbt_stop=findViewById(R.id.bt_stop);

handler=newHandler(newHandler.Callback(){

@Override

publicbooleanhandleMessage(@NonNullMessagemsg){

//后台线程通过Handler对象调用sendMessage()方法发送消息后产生的回调

floatf=CounterThread.getMessageData(msg);

tv.setText(String.format("%.2f",f));//更新计数器值

returntrue;//Handler事件不再往下传递

}

});

bt_start.setOnClickListener(newView.OnClickListener(){

@Override

publicvoidonClick(Viewv){

bt_start.setEnabled(false);//反转两个Button的使能状态

bt_stop.setEnabled(true);

thread=newCounterThread(handler);

thread.start();//启动线程run()方法,run()结束时线程消亡

//不能调用thread.run(),该方法会在主UI中执行,而不是开辟后台线程执行

}

});

bt_stop.setOnClickListener(newView.OnClickListener(){

@Override

publicvoidonClick(Viewv){

bt_start.setEnabled(true);

bt_stop.setEnabled(false);

thread.stopCounter();

//修改CounterThread的isRunning为false,循环结束,线程运行结束

}

});

}

}5.2任务2-使用多线程与自定义接口81使用自定义接口产生回调和数据回传2后台线程中切换UI线程3理解接口与回调使用自定义接口的线程CounterThread9publicclassCounterThreadextendsThread{

privateAtomicBooleanisRunning;//原子变量多线程操作是安全的

privateActivityactivity;//使用Activity对象切换UI线程

privateOnUpdateListeneronUpdateListener;//定义自定义接口对象

privatefloatcounter;//全局变量counter在切换线程的匿名run()方法中被访问

publicCounterThread(Activityactivity){

this.activity=activity;//Activity对象从外部传入

//可直接传MainActivity.this

}

publicinterfaceOnUpdateListener{

voidonUpdate(floatcounter);//利用自定义接口传递计数器值

}

publicvoidsetOnUpdateListener(OnUpdateListeneronUpdateListener){

this.onUpdateListener=onUpdateListener;//传递外部实现的接口

//外部实现的接口得到计数器值,并更新UI

}

@Override

publicvoidrun(){

isRunning=newAtomicBoolean(true);

counter=0.0f;

while(isRunning.get()){

try{

Thread.sleep(10);//睡眠10ms,可能会有异常,用try-catch捕捉异常

}catch(InterruptedExceptione){

e.printStackTrace();

}

counter+=0.01f;

activity.runOnUiThread(newRunnable(){

//利用Activity对象切换到主UI线程

//后台线程Thread不能更新UI

@Override

publicvoidrun(){

if(onUpdateListener!=null){

onUpdateListener.onUpdate(counter);

//通过自定义接口将计数器值传给接口实现者,并更新UI

}

}

});

}

}

publicvoidstopCounter(){

isRunning.set(false);

//将isRunning设置为false,使得run()循环结束

}

}实现MainActivity10publicclassMainActivityextendsAppCompatActivity{

CounterThreadthread;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.my_main);

TextViewtv=findViewById(R.id.tv_result);

Buttonbt_start=findViewById(R.id.bt_start);

Buttonbt_stop=findViewById(R.id.bt_stop);

bt_start.setOnClickListener(newView.OnClickListener(){

@Override

publicvoidonClick(Viewv){

bt_start.setEnabled(false);//反转两个Button的使能状态

bt_stop.setEnabled(true);

thread=newCounterThread(MainActivity.this);

thread.setOnUpdateListener(newCounterThread.OnUpdateListener(){

//设置线程的接口回调,在回调方法中得到计数器值,并更新UI

@Override

publicvoidonUpdate(floatcounter){

tv.setText(String.format("%.2f",counter));

}

});

thread.start();//启动后台线程

}

});

bt_stop.setOnClickListener(newView.OnClickListener(){

@Override

publicvoidonClick(Viewv){

bt_start.setEnabled(true);

bt_stop.setEnabled(false);

thread.stopCounter();

}

});

}

}5.3任务3-使用多线程与LiveData111视图模型与LiveData2LiveData的更新值与Observer侦听3LiveData的生命周期LiveData的特点与使用方法LiveData可以包含任何类型的数据,并在数据发生变化的时候通知观察者,适合与ViewModel(视图模型)结合在一起使用,可以让ViewModel将数据的变化主动通知给Activity或者FragmentLiveData本身是抽象类,一般会使用实现类MutableLiveData<T>定义LiveData数据改值方法setValue(),在UI线程中使用postValue(),在后台线程和UI线程中均可使用在UI线程中观测值的变化实现Observer接口在onChanged()回调中取更新后的值,更新UILiveData的生命周期跟随着视图模型,不会随着后台线程消亡而消亡视图模型生命周期跟随着视图模型的拥有者12创建自定义视图模型CounterViewModel13publicclassCounterViewModelextendsViewModel{

//在自定义的CounterViewModel中定义MutableLiveData

privateMutableLiveData<Float>counter;

//MutableLiveData数据需要泛型定义数据类型,泛型只接受类,float对应类是Float

publicCounterViewModel(){

counter=newMutableLiveData<>();//对counter实例化

counter.setValue(0.0f);//设置初始值

}

publicMutableLiveData<Float>getCounter(){

returncounter;

}

}CounterViewModelcounterViewModel=newViewModelProvider(owner).get(CounterViewModel.class);实现后台线程CounterThread14publicclassCounterThreadextendsThread{

privateViewModelStoreOwnerowner;

privateAtomicBooleanisRunning;

privateMutableLiveData<Float>counter;

privateintmode;

publicstaticfinalintMODE_RESTART=0;//计数器清零模式

publicstaticfinalintMODE_RESUME=1;//计数器暂停模式

publicCounterThread(ViewModelStoreOwnerowner,intmode){

this.owner=owner;

//可以不传owner,而是直接传ViewModel对象

//使用传参owner,验证后台线程与前端UI使用同一个owner获得的视图模型是否相同

//相同视图模型实例对应的LiveData是同一个对象

this.mode=mode;

}

publicCounterThread(ViewModelStoreOwnerowner){

this.owner=owner;

mode=MODE_RESTART;

}

@Override

publicvoidrun(){

isRunning=newAtomicBoolean(true);

CounterViewModelcounterViewModel=newViewModelProvider(owner)

.get(CounterViewModel.class);

//从CounterViewModel中获得计数器变量,该变量具有Observer接口

//在主UI中,可对该变量实现Observer接口,感知变量的变动

counter=counterViewModel.getCounter();

//counter不会随线程消亡,而是在owner的生命周期中一直存在

if(mode==MODE_RESTART){

//在MODE_RESTART模式,计数器重置为0

counter.postValue(0.0f);

//在线程中使用postValue()改值,在主UI中,setValue()和postValue()均可

}

while(isRunning.get()){

try{

Thread.sleep(10);

}catch(InterruptedExceptione){

e.printStackTrace();

}

counter.postValue(counter.getValue()+0.01f);

//在主UI中通过counter的Observer接口和回调方法取出更新值,更新UI

}

}

publicvoidstopCounter(){

isRunning.set(false);

}

}实现MainActivity15publicclassMainActivityextendsAppCompatActivity{

CounterThreadthread;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.my_main);

CounterViewModelcounterViewModel=newViewModelProvider(this)

.get(CounterViewModel.class);

//从CounterViewModel中获得MutableLiveData数据,生命周期同MainActivity

MutableLiveData<Float>counter=counterViewModel.getCounter();

TextViewtv=findViewById(R.id.tv_result);

//MutableLiveData数据具有侦听功能,在Observer接口中感知数据的变动

counter.observe(this,newObserver<Float>(){

@Override

publicvoidonChanged(FloataFloat){

Strings=String.format("%.2f",aFloat);

tv.setText(s);

}

});

Buttonbt_start=findViewById(R.id.bt_start);

Buttonbt_stop=findViewById(R.id.bt_stop);

bt_stop.setOnClickListener(newView.OnClickListener(){

@Override

publicvoidonClick(Viewv){

bt_start.setText("Start");//bt_start须恢复成START按钮

bt_stop.setEnabled(false);

thread.stopCounter();

}

});

bt_start.setOnClickListener(newView.OnClickListener(){

@Override

publicvoidonClick(Viewv){

//状态:Start->Pause->Resume->Pause->Resume...

//或者Start->(Stop)->Start

Stringstart_tag=bt_start.getText().toString();

if(start_tag.equalsIgnoreCase("Start")){

thread=newCounterThread(MainActivity.this);

bt_stop.setEnabled(true);

bt_start.setText("Pause");

thread.start();

}elseif(start_tag.equalsIgnoreCase("Pause")){

thread.stopCounter();

bt_stop.setEnabled(false);

bt_start.setText("Resume");

}else{

thread=newCounterThread(MainActivity.this,

CounterThread.MODE_RESUME);

//使用MODE_RESUME,计数器值不清0,在上一次取值基础上计数

bt_start.setText("Pause");

bt_stop.setEnabled(true);

thread.start();

}

}

});

}

}5.4任务4-使用Okhttp和Gson获取WebAPI数据161WebAPI与JSON数据2Okhttp的使用方法3JSON数据解析方法Gradle依赖和上网权限ModuleGradle文件添加Okhttp和Gson依赖implementation'com.squareup.okhttp3:okhttp:4.9.3’implementation'com.google.code.gson:gson:2.9.0'添加Internet上网权限AndroidManifest.xml文件中添加静态权限<uses-permissionandroid:name="android.permission.INTERNET"></uses-permission>Android10.0+默认只能访问Https,不能访问Http,需要额外设置AndroidManifest.xml文件的application中增加访问明码网络属性设置android:usesCleartextTraffic="true"17JSON数据JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式,比XML更小、更快以及更易于解析,常在WebAPI中使用JSON数据分为对象和数组对象花括号{},使用key-value的键值形式定义key与value之间用冒号(:)隔开一个对象可拥有多个字段,不同字段间用逗号(,)隔开key使用字符串数据Value数值型数据(整数或浮点数,无双引号包围)字符串数据(由双引号包围)逻辑值数据(true或false)数组数据(由方括号包围,元素间用逗号隔开)Null对象数据(由花括号包围)数组由方括号包围数组可以嵌套数组或者对象18[{"id":1,"name":"北京"},…{"id":17,"name":"浙江"},…][{"id":999,"name":"杭州","weather_id":"CN101210101"},{"id":1000,"name":"萧山","weather_id":"CN101210102"},…{"id":1006,"name":"富阳","weather_id":"CN101210108"}]JSON数据解析-City.java19importcom.google.gson.annotations.SerializedName;

publicclassCity{

publicStringname;

publicintid;

@SerializedName("weather_id")

publicStringweatherId;

//若JSON数据的字段"weather_id"与类成员weatherId不符,可用@SerializedName映射

@Override

publicStringtoString(){

returnString.format("%s,id=%d",name,id);

}

publicCity(){

name="";

weatherId="";

}

publicCity(Stringname,intid,StringweatherId){

this.name=name;

this.id=id;

this.weatherId=weatherId;

}

}自定义JSON解析工具类CityParsingUtils.java20publicclassCityParsingUtils{

/**需要在ModuleGradle文件的dependencies中增加Gson

implementation'com.google.code.gson:gson:2.9.0'

*/

publicstaticList<City>json2ListByJsonObj(Strings)throwsJSONException{

//若遇到错误,抛出异常,由调用者使用try-catch捕捉异常

//直接采用JsonObject解析

List<City>list=newArrayList<>();

if(!TextUtils.isEmpty(s)){

JSONArrayjsonArray=newJSONArray(s);

for(inti=0;i<jsonArray.length();i++){

JSONObjectjsonObject=jsonArray.getJSONObject(i);

intid=jsonObject.getInt("id");

Stringname=jsonObject.getString("name");

StringweatherId="";

if(jsonObject.toString().toLowerCase().contains("weather_id")){

//JSON数据有些包含weather_id字段,有些不包含,处理比较被动

//需要将JSON对象转换成string,对字段进行包含判断

weatherId=jsonObject.getString("weather_id");

}

Citycity=newCity(name,id,weatherId);

list.add(city);

}

}

returnlist;

}

publicstaticList<City>json2ListByGson(Strings)throwsJSONException{

List<City>list=newArrayList<>();

if(!TextUtils.isEmpty(s)){

JSONArrayjsonArray=newJSONArray(s);

for(inti=0;i<jsonArray.length();i++){

//从JSON数组中遍历JSON对象,对JSON对象利用Gson转换成对应类

Strings1=jsonArray.get(i).toString();

//JSON对象再转换成JSON字符串供Gson转换

Citycity=newGson().fromJson(s1,City.class);

//若City.class中定义的某些字段和JSON字段没有匹配,只取能匹配的字段

list.add(city);

}

}

returnlist;

}

publicstaticList<City>json2ListByGsonList(Strings)throwsException{

//定义方法时,增加throwsException,以便于调用时捕捉异常

List<City>list=newArrayList<>();

if(!TextUtils.isEmpty(s)){

Typetype=newTypeToken<List<City>>(){}.getType();

//利用com.google.gson.reflect.TypeToken反射构造List<City>列表对象类数据

list=newGson().fromJson(s,type);//直接利用TypeToken转换为列表对象

}

returnlist;

}

}自定义Okhttp调用工具类WebApiUtils.java21

publicstaticList<City>getApiDataBlock(Stringurl)throwsException{

//在运行过程中遇到异常,将抛出异常给调用者处理

//采用同步的方式,代码将阻塞,适合其他后台线程循环调用此方法获取批量请求结果

OkHttpClientclient=getClient();

Requestrequest=newRequest.Builder().url(url).get().build();

Callcall=client.newCall(request);

//生成Request和Call对象,与异步调用相同,区别的是call的处理方式

Responseresponse=call.execute();

//同步方法,运行该代码将进入阻塞,直至call执行完毕

Strings=response.body().string();

List<City>list=CityParsingUtils.json2ListByGson(s);

//调用CityParsingUtils第二种解析方法,可尝试更换成其他方法

returnlist;

}

}publicclassWebApiUtils{

/**需要在ModuleGradle文件的dependencies标签中增加Okhttp组件

implementation'com.squareup.okhttp3:okhttp:4.9.3'

*/

privatestaticOkHttpClientclient;

//client在WebApiUtils中是单例模式,始终只有1个对象

privatestaticOkHttpClientgetClient(){

synchronized(WebApiUtils.class){

//加锁避免多个线程同时调用getClient(),在client==null时重复生成实例

//对WebApiUtils.class加锁,多个线程只能有1个线程能访问此代码

if(client==null){

client=newOkHttpClient();

}

returnclient;

}

}

publicinterfaceOnReadFinishedListener{

//自定义接口,定义两个回调,分别用于读取成功和出错处理

publicvoidonFinished(List<City>readOutList);

//读取成功,将Json数据转换为类列表数据返回

publicvoidonFail(Stringe);

//读取失败,回传错误信息

}

publicstaticvoidgetApiDataAsync(){…}自定义Okhttp调用工具类WebApiUtils(续)22

publicstaticvoidgetApiDataAsync(Activityactivity,Stringurl,

OnReadFinishedListenerl){

//传入Activity对象,用于切换线程

OkHttpClientc=getClient();//通过调用getClient()得到OkHttpClient单例

Requestrequest=newRequest.Builder().url(url).get().build();

//构造一个请求对象Request,url()传url网址,get()是GET请求的方法

//请求方法有get()、post()、delete()、put()等方法

Callcall=client.newCall(request);//利用Request生成一个call对象

//每一次访问对应一个call对象,异步访问时将call对象加入队列

call.enqueue(newCallback(){

@Override

publicvoidonFailure(@NonNullCallcall,@NonNullIOExceptione){

activity.runOnUiThread(newRunnable(){//切换到UI线程

@Override

publicvoidrun(){

l.onFail(e.toString());

//通过自定义接口将错误信息通过onFail()传递到UI线程

}

});

}

@Override

publicvoidonResponse(@NonNullCallcall,

@NonNullResponseresponse){

//修改原回调方法,去除throws异常语句,直接捕捉处理

try{

Strings=response.body().string();//得到响应的文本

//注意是string()方法,不是toString()方法

List<City>list=CityParsingUtils.json2ListByGsonList(s);

//CityParsingUtils提供了3种解析方法,可调用任何一种

activity.runOnUiThread(newRunnable(){//切换到UI线程

@Override

publicvoidrun(){

l.onFinished(list);

}

});

}catch(Exceptione){

e.printStackTrace();

activity.runOnUiThread(newRunnable(){//切换到UI线程

@Override

publicvoidrun(){

l.onFail(e.toString());//错误信息通过接口回调传给调用者

}

});

}

}

});

}实现MainActivity布局my_main.xml23<LinearLayoutxmlns:android="/apk/res/android"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent">

<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="YournameandID"/>

<EditText

android:id="@+id/et_url"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:ems="10"

android:inputType="textPersonName"

android:text="4:8080/api/china/17"/>

<Button

android:id="@+id/bt_async"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="Asyncmode"/>

<Button

android:id="@+id/bt_block"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="Blockmode"/>

<ListView

android:id="@+id/listView"

android:layout_width="match_parent"

android:layout_height="match_parent"/>

</LinearLayout>实现MainActivity24publicclassMainActivityextendsAppCompatActivity{

ListViewlv;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.my_main);

EditTextet=findViewById(R.id.et_url);

lv=findViewById(R.id.listView);

findViewById(R.id.bt_async)

.setOnClickListener(newView.OnClickListener(){

@Override

publicvoidonClick(Viewview){

Stringurl=et.getText().toString().trim();

//trim()方法将字符串头尾多余空格去除

//以下是异步调用的方法

WebApiUtils.getApiDataAsync(MainActivity.this,url,

newWebApiUtils.OnReadFinishedListener(){

@Override

publicvoidonFinished(List<City>readOutList){

showList(readOutList);

}

@Override

publicvoidonFail(Stringe){

showToast(e);

}

});

}

});

findViewById(R.id.bt_block)…//代码见另一页

}

privatevoidshowList(List<City>readOutList){

//将列表数据生成适配器,将适配器显示到ListView组件上

ArrayAdapter<City>adapter=newArrayAdapter<>(this,

android.R.layout.simple_list_item_1,readOutList);

lv.setAdapter(adapter);

}

privatevoidshowToast(Stringe){

Toast.makeText(this,e,Toast.LENGTH_LONG).show();

}

}实现MainActivity(续)25findViewById(R.id.bt_block)

.setOnClickListener(newView.OnClickListener(){

@Override

publicvoidonClick(Viewview){

Stringurl=et.getText().toString().trim();

//同步调用,在主UI中无法直接调用,需要启动一个线程来调用

newThread(newRunnable(){

@Override

publicvoidrun(){

try{

List<City>list=WebApiUtils.getApiDataBlock(url);

MainActivity.this.runOnU

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论