Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android 百度地圖離線地圖功能

android 百度地圖離線地圖功能

編輯:關於Android編程

最近做了一個百度地圖離線地圖的功能,雖然功能實現了,但過程中也碰到了一些問題。首先,看看效果圖吧。
這裡寫圖片描述 這裡寫圖片描述 這裡寫圖片描述

1、離線地圖相關API

API地址:http://wiki.lbsyun.baidu.com/cms/androidsdk/doc/v4_0_0/index.html

MKOfflineMap類

主要的一個類,提供了離線地圖的管理功能,例如,下載,暫停、更新,刪除等功能。每次只允許一個下載任務進行,後面的需要排隊。

void destroy()
銷毀離線地圖管理模塊,不用時調用 java.util.ArrayList< MKOLUpdateElement> getAllUpdateInfo()
返回各城市離線地圖更新信息,已下載的離線地圖 java.util.ArrayList< MKOLSearchRecord> getHotCityList()
返回熱門城市列表 java.util.ArrayList getOfflineCityList()
返回支持離線地圖城市列表 MKOLUpdateElement getUpdateInfo(int cityID)
返回指定城市ID離線地圖更新信息 boolean init(MKOfflineMapListener listener)
初使化 boolean pause(int cityID)
暫停下載或更新指定城市ID的離線地圖 boolean remove(int cityID)
刪除指定城市ID的離線地圖 java.util.ArrayList< MKOLSearchRecord> searchCity(java.lang.String
cityName)
根據城市名搜索該城市離線地圖記錄 boolean start(int cityID)
啟動下載指定城市ID的離線地圖,或在暫停更新某城市後繼續更新下載某城市離線地圖 boolean update(int cityID)
啟動更新指定城市ID的離線地圖

MKOLSearchRecord類

離線地圖搜索城市記錄結構

java.util.ArrayList< MKOLSearchRecord> childCities
子城市列表 int cityID
城市ID java.lang.String cityName
城市名稱 int cityType
城市類型0:全國;1:省份;2:城市,如果是省份,可以通過childCities得到子城市列表 int size
數據包總大小

MKOLUpdateElement類

離線地圖更新信息,下面是其中的一些字段

int cityID
城市ID java.lang.String cityName
城市名稱 static int DOWNLOADING
正在下載 LatLng geoPt
城市中心點坐標 int level
離線包地圖層級 int ratio
下載比率,100為下載完成 int serversize
服務端數據大小 int size
已下載數據大小 int status
下載狀態 boolean update
是否為更新

MKOfflineMapListener接口

該接口返回新安裝離線地圖、下載更新、數據版本更新等結果,用戶需要實現該接口以處理相應事件。裡面有一個唯一的方法:

void onGetOfflineMapState(int type, int state) 返回通知事件

2、主要代碼

OfflineActivity類

/* 此Demo用來演示離線地圖的下載和顯示 */
public class OfflineActivity extends Activity implements MKOfflineMapListener {

    private MKOfflineMap mOffline = null;
    private TextView cidView;
    private TextView stateView;
    private EditText cityNameView;
    private HashMap hashMap = new HashMap(); //是否已下載;
    private CityExpandableListAdapter adapter;
    private HotcityListAdapter hAdapter;
    private OfflineHandler offlineHandler;
    private MKOLSearchRecord currentRecord;
    private ArrayList loadingList = new ArrayList();
    private ArrayList loadedList = new ArrayList();
    public HashMap clickMap;
    /**
     * 已下載的離線地圖信息列表
     */
    public ArrayList localMapList = null;
    private LocalMapAdapter lAdapter = null;
    private loadingMapAdapter dAdapter = null;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_offline);

        ImageButton back = (ImageButton) findViewById(R.id.back);
        back.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                OfflineActivity.this.finish();
            }
        });
        offlineHandler = new OfflineHandler(this);


        mOffline = new MKOfflineMap();
        mOffline.init(this);
        initView();

        initCurLocation();
    }

    LocationManager lm = null; // location管理器
    LocationClient mLocClient;
    private void initCurLocation()
    {
        lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        if (lm != null) {

            // 定位初始化
            mLocClient = new LocationClient(this);
            mLocClient.registerLocationListener(new MyLocationListenner());
            LocationClientOption option = new LocationClientOption();
            option.setOpenGps(true);// 打開gps
            option.setCoorType("bd09ll"); // 設置坐標類型
            option.setPriority(LocationClientOption.NetWorkFirst);//設置網絡優先(不設置,默認是gps優先)
            option.setAddrType("all");// 返回的定位結果包含地址信息
            option.setScanSpan(10000);// 設置發起定位請求的間隔時間為10s(小於1秒則一次定位)
            mLocClient.setLocOption(option);
            mLocClient.start();

        }else {
            SystemUtil.showMessage("請打開GPS定位設置");
        }
    }

    public void setCurrentLocation(String currentLocation) {
        TextView current = (TextView) findViewById(R.id.current_name);
        current.setText(currentLocation);
        currentRecord = search(currentLocation);
        RelativeLayout currentItem = (RelativeLayout)findViewById(R.id.current_item);
        currentItem.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (hashMap.get(currentRecord.cityName))
                {
                    Toast.makeText(OfflineActivity.this, "離線地圖已下載", Toast.LENGTH_LONG).show();
                }else {
                    TextView currentSize = (TextView) v.findViewById(R.id.current_size);
                    currentSize.setText("正在下載");

                    start(currentRecord.cityID);
                }
            }
        });
    }

    public class MyLocationListenner implements BDLocationListener {

        @Override
        public void onReceiveLocation(BDLocation location) {
            MyLocationData locData = new MyLocationData.Builder()
                    .accuracy(location.getRadius())
                            // 此處設置開發者獲取到的方向信息,順時針0-360
                    .direction(100).latitude(location.getLatitude())
                    .longitude(location.getLongitude()).build();
            String address=location.getAddrStr();
            String city=location.getCity();

//            System.out.println("地址:"+address+"城市:"+city);
            setCurrentLocation(city);
        }

        public void onReceivePoi(BDLocation poiLocation) {
        }
    }
    private void initView() {

        // 獲取已下過的離線地圖信息
        localMapList = mOffline.getAllUpdateInfo();
        if (localMapList == null) {
            localMapList = new ArrayList();
        }

        ListView localMapListView = (ListView) findViewById(R.id.localmaplist);
        lAdapter = new LocalMapAdapter();
        localMapListView.setAdapter(lAdapter);

        ListView loadingListView = (ListView)findViewById(R.id.lodinglist);
        dAdapter = new loadingMapAdapter();
        loadingListView.setAdapter(dAdapter);

//        cidView = (TextView) findViewById(R.id.cityid);
//        cityNameView = (EditText) findViewById(R.id.city);
//        stateView = (TextView) findViewById(R.id.state);

        ListView hotCityList = (ListView) findViewById(R.id.hotcitylist);
        final ArrayList hotCities = new ArrayList();
        // 獲取熱門城市列表
        final ArrayList records1 = mOffline.getHotCityList();
        if (records1 != null) {
            for (MKOLSearchRecord r : records1) {
                hotCities.add(r.cityID);
            }
        }
        hAdapter = new HotcityListAdapter(this, records1,hashMap);

        hotCityList.setAdapter(hAdapter);


        hotCityList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                if (hashMap.get(records1.get(position).cityName))
                {
                    Toast.makeText(OfflineActivity.this, "離線地圖已下載", Toast.LENGTH_LONG).show();

                }else {
                    TextView childSize = (TextView) view.findViewById(R.id.child_size);
                    childSize.setText("正在下載");
                    start(records1.get(position).cityID);
                }

            }
        });

        ExpandableListView allCityList = (ExpandableListView) findViewById(R.id.allcitylist);
        // 獲取所有支持離線地圖的城市
        final ArrayList records2 = mOffline.getOfflineCityList();
        clickMap = new HashMap();
        if (records1 != null) {
            for (MKOLSearchRecord r : records2) {
//                allCities.add(r.cityName+"--" + this.formatDataSize(r.size));
//                allCitiyIds.add(r.cityID);
                hashMap.put(r.cityName,downList(r.cityName));
                clickMap.put(r.cityName, "0");
                if (r.childCities != null && r.childCities.size() != 0)
                {
                    ArrayList childrecord = r.childCities;
//
                    for (MKOLSearchRecord cr : childrecord)
                    {
                        hashMap.put(cr.cityName,downList(cr.cityName));
                    }
                }

            }
        }

        adapter = new CityExpandableListAdapter(this,records2,hashMap);
        allCityList.setAdapter(adapter);
        allCityList.setGroupIndicator(null);

        hAdapter.notifyDataSetChanged();

        allCityList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {

                if (records2 != null )
                {
                    MKOLSearchRecord record = records2.get(groupPosition);
                    if (record.childCities == null)
                    {
                        if (hashMap.get(record.cityName))
                        {
                            Toast.makeText(OfflineActivity.this, "離線地圖已下載", Toast.LENGTH_LONG).show();
                        }else
                        {
//                            System.out.println("simplename:"+v.getClass().getSimpleName());

                            int cd = record.cityID;
                            start(cd);
                           /* int size = ((ViewGroup)v).getChildCount();
                            for (int i = 0 ; i< size; i++)
                            {
                                View child = ((ViewGroup)v).getChildAt(i);
                                System.out.println("simplename:"+child.getClass().getSimpleName());
                            }
                            View child = ((ViewGroup)v).getChildAt(1);
                            ((TextView)child).setText("正在下載");*/

                            clickMap.put(record.cityName, "1");
//                            adapter.notifyDataSetChanged();

                        }
                    }
                }
                return false;
            }
        });

        allCityList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {

                if (records2 != null)
                {
                    MKOLSearchRecord record = records2.get(groupPosition);
                    MKOLSearchRecord cldred = record.childCities.get(childPosition);
                    if (hashMap.get(cldred.cityName))
                    {
                        Toast.makeText(OfflineActivity.this, "離線地圖已下載", Toast.LENGTH_LONG).show();

                    }else {
                        TextView childSize = (TextView) v.findViewById(R.id.child_size);
                        childSize.setText("正在下載");

                        start(cldred.cityID);
                    }

                }

                return false;
            }
        });

        LinearLayout cl = (LinearLayout) findViewById(R.id.citylist_layout);
        LinearLayout lm = (LinearLayout) findViewById(R.id.localmap_layout);
        lm.setVisibility(View.GONE);
        cl.setVisibility(View.VISIBLE);



    }

    public boolean downList(String cityName)
    {
        Boolean flag = false;
        if (localMapList != null)
        {
            for (int i = 0; i  records = mOffline.searchCity(city);
        if (records == null || records.size() != 1) {
            return null;
        }
//        cidView.setText(String.valueOf(records.get(0).cityID));
        TextView current_size = (TextView) findViewById(R.id.current_size);
        if (hashMap.get(records.get(0).cityName))
        {
            current_size.setText("已下載");

        }else {
            current_size.setText(formatDataSize(records.get(0).size));
        }
        return records.get(0);
    }

    /**
     * 開始下載
     *
     * @param
     */
    public void start(int cityid) {
//        int cityid = Integer.parseInt(cidView.getText().toString());
        mOffline.start(cityid);
        clickLocalMapListButton(null);
//        Toast.makeText(this, "開始下載離線地圖. cityid: " + cityid, Toast.LENGTH_SHORT).show();
        updateView(null, false);
    }

    /**
     * 暫停下載
     *
     * @param view
     */
    public void stop(View view) {
        int cityid = Integer.parseInt(cidView.getText().toString());
        mOffline.pause(cityid);
        Toast.makeText(this, "暫停下載離線地圖. cityid: " + cityid, Toast.LENGTH_SHORT)
                .show();
        updateView(null, false);
    }

    /**
     * 刪除離線地圖
     *
     * @param view
     */
    public void remove(View view) {
        int cityid = Integer.parseInt(cidView.getText().toString());
        mOffline.remove(cityid);

        Toast.makeText(this, "刪除離線地圖. cityid: " + cityid, Toast.LENGTH_SHORT)
                .show();
        updateView(null,false);
    }

    /**
     * 更新狀態顯示
     */
    public void updateView(MKOLUpdateElement element, boolean flag) {
        localMapList = mOffline.getAllUpdateInfo();
        if (localMapList == null) {
            localMapList = new ArrayList();
        }
        loadingList.clear();
        loadedList.clear();
        for (MKOLUpdateElement element1 : localMapList)
        {
            if (element1.ratio != 100)
            {
                loadingList.add(element1);
            }else {
                loadedList.add(element1);
            }
        }



        if (element != null)
        {
            hashMap.put(element.cityName, flag);
            if (currentRecord.cityID == element.cityID)
            {

                TextView currentSize = (TextView) findViewById(R.id.current_size);
                if(flag)
                {
                    currentSize.setText("已下載");
                }else {
                    currentSize.setText(formatDataSize(element.size));
                }
            }else {

                adapter.notifyDataSetChanged();
                hAdapter.notifyDataSetChanged();
            }
        }

        lAdapter.notifyDataSetChanged();
        dAdapter.notifyDataSetChanged();
    }

    @Override
    protected void onPause() {
//        int cityid = Integer.parseInt(cidView.getText().toString());
//        MKOLUpdateElement temp = mOffline.getUpdateInfo(cityid);
//        if (temp != null && temp.status == MKOLUpdateElement.DOWNLOADING) {
//            mOffline.pause(cityid);
//        }
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    public String formatDataSize(int size) {
        String ret = "";
        if (size < (1024 * 1024)) {
            ret = String.format("%dK", size / 1024);
        } else {
            ret = String.format("%.1fM", size / (1024 * 1024.0));
        }
        return ret;
    }

    @Override
    protected void onDestroy() {
        /**
         * 退出時,銷毀離線地圖模塊
         */
        mOffline.destroy();
        super.onDestroy();
    }

    @Override
    public void onGetOfflineMapState(int type, int state) {
        switch (type) {
            case MKOfflineMap.TYPE_DOWNLOAD_UPDATE: {
                MKOLUpdateElement update = mOffline.getUpdateInfo(state);
                // 處理下載進度更新提示
                if (update != null) {

//                    stateView.setText(String.format("%s : %d%%", update.cityName,
//                            update.ratio));
//                    System.out.println("ratio:"+update.ratio);
                    if (update.ratio == 100)
                    {
                        updateView(update,true);
                    }else {
                        updateView(null, false);
                    }
                }
            }
                break;
            case MKOfflineMap.TYPE_NEW_OFFLINE:
                // 有新離線地圖安裝
                Log.d("OfflineDemo", String.format("add offlinemap num:%d", state));
                break;
            case MKOfflineMap.TYPE_VER_UPDATE:
                // 版本更新提示
                // MKOLUpdateElement e = mOffline.getUpdateInfo(state);

                break;
            default:
                break;
        }

    }
    /**
     * 正在下載城市列表適配器
     */
    public class loadingMapAdapter extends BaseAdapter{

        @Override
        public int getCount() {
            return loadingList.size();
        }

        @Override
        public Object getItem(int position) {
            return loadingList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null)
            {
                convertView = LayoutInflater.from(OfflineActivity.this).inflate(R.layout.loding_list, null);
            }
            TextView name = (TextView) convertView.findViewById(R.id.city_name);
            TextView size = (TextView)convertView.findViewById(R.id.city_size);
            TextView ratio = (TextView)convertView.findViewById(R.id.down_ratio);
            ImageButton manager= (ImageButton) convertView.findViewById(R.id.down_manager);
            final MKOLUpdateElement ele = loadingList.get(position);
            name.setText(ele.cityName);
            size.setText(formatDataSize(ele.size));
            ratio.setText(ele.ratio+"%");

            manager.setOnClickListener(new OnClickListener() {
                boolean flag = true;
                @Override
                public void onClick(View v) {
                    if (flag) {
                        mOffline.pause(ele.cityID);
                        v.setBackgroundResource(R.drawable.loading_start);
                        flag = false;
                    }else {
                        mOffline.start(ele.cityID);
                        v.setBackgroundResource(R.drawable.loading_pause);
                        flag = true;
                    }
                }
            });
            return convertView;
        }
    }

    /**
     * 離線地圖管理列表適配器
     */
    public class LocalMapAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return loadedList.size();
        }

        @Override
        public Object getItem(int index) {
            return loadedList.get(index);
        }

        @Override
        public long getItemId(int index) {
            return index;
        }

        @Override
        public View getView(int index, View view, ViewGroup arg2) {
            MKOLUpdateElement e = (MKOLUpdateElement) getItem(index);
            view = View.inflate(OfflineActivity.this,
                    R.layout.offline_localmap_list, null);
            initViewItem(view, e);
            return view;
        }

        void initViewItem(View view, final MKOLUpdateElement e) {
            Button remove = (Button) view.findViewById(R.id.remove);
            TextView title = (TextView) view.findViewById(R.id.title);
            TextView update = (TextView) view.findViewById(R.id.update);
//            TextView ratio = (TextView) view.findViewById(R.id.ratio);
            Button doUpdate = (Button) view.findViewById(R.id.exe_update);
//            ratio.setText(e.ratio + "%");
            title.setText(e.cityName);
            if (e.update) {
                update.setText("可更新");
            } else {
                update.setText("最新");
            }

            remove.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View arg0) {
                    mOffline.remove(e.cityID);
                    clickMap.put(e.cityName,"0");
                    updateView(e, false);
                }
            });
            doUpdate.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mOffline.update(e.cityID);
                }
            });
        }

    }

}

布局文件activity_offline.xml



    
        
        

CityExpandableListAdapter類

public class CityExpandableListAdapter extends BaseExpandableListAdapter {

    private OfflineActivity context;
    private ArrayList records;
    private HashMap hashMap;

    public CityExpandableListAdapter(OfflineActivity context, ArrayList records, HashMap hashMap)
    {
        this.context = context;
        this.records = records;
        this.hashMap = hashMap;
        System.out.println("hashMapsize:"+hashMap.size());
    }

    @Override
    public int getGroupCount() {
        return records.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        if (records.get(groupPosition).childCities != null)
        {
            return records.get(groupPosition).childCities.size();
        }
        return 0;
    }

    @Override
    public Object getGroup(int groupPosition) {
        return records.get(groupPosition);
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        if (records.get(groupPosition).childCities != null)
        {
            return records.get(groupPosition).childCities.get(childPosition);
        }
        return null;
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        GroupHolder groupHolder = null;
        if (convertView == null)
        {
            convertView = LayoutInflater.from(context).inflate(R.layout.expandlist_group, parent, false);
            groupHolder = new GroupHolder();
            groupHolder.txt = (TextView)convertView.findViewById(R.id.names);
            groupHolder.size = (TextView)convertView.findViewById(R.id.map_size);
            groupHolder.img = (ImageView)convertView.findViewById(R.id.indicator_arrow);
            convertView.setTag(groupHolder);
        }
        else
        {
            groupHolder = (GroupHolder)convertView.getTag();
        }

        String cityName = records.get(groupPosition).cityName;
        groupHolder.txt.setText(cityName);
        if (records.get(groupPosition).childCities == null)
        {
            groupHolder.img.setVisibility(View.GONE);
            groupHolder.size.setVisibility(View.VISIBLE);
            if (hashMap.get(cityName))
            {
                groupHolder.size.setText("已下載");
            }else {
                if ("1".equals(context.clickMap.get(cityName)))
                {
                    groupHolder.size.setText("正在下載");
                }else {
                    groupHolder.size.setText(context.formatDataSize(records.get(groupPosition).size));
                }
            }

        }else {
            groupHolder.size.setVisibility(View.GONE);
            groupHolder.img.setVisibility(View.VISIBLE);
        }

        //判斷isExpanded就可以控制是按下還是關閉,同時更換圖片
        if(isExpanded){
            groupHolder.img.setBackgroundResource(R.drawable.moreitems_arrow);
        }else{
            groupHolder.img.setBackgroundResource(R.drawable.moreitems_arrow_down); }
        return convertView;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        ItemHolder itemHolder = null;
        if (convertView == null)
        {
//            convertView = convertView.inflate(context, R.layout.expandlist_item, null);
            convertView = LayoutInflater.from(context).inflate(R.layout.expandlist_item, parent,false);
            itemHolder = new ItemHolder();
            itemHolder.txt = (TextView)convertView.findViewById(R.id.child_names);
            itemHolder.size  = (TextView)convertView.findViewById(R.id.child_size);
//            itemHolder.img = (ImageView)convertView.findViewById(R.id.img);
            convertView.setTag(itemHolder);
        }
        else
        {
            itemHolder = (ItemHolder)convertView.getTag();
        }
        if (records.get(groupPosition).childCities != null)
        {
            ArrayList list = records.get(groupPosition).childCities;
            if (list.size()>0)
            {
                MKOLSearchRecord info = list.get(childPosition);
                String cityName  = info.cityName;
                itemHolder.txt.setText(cityName);
                if (hashMap.get(cityName))
                {
                    itemHolder.size.setText("已下載");
                }else {
                    itemHolder.size.setText(context.formatDataSize(info.size));
                }
            }
        }

        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }

    class GroupHolder
    {
        public TextView txt;
        public TextView size;
        public ImageView img;
    }

    class ItemHolder
    {
        public ImageView img;
        public TextView size;
        public TextView txt;
    }

}

3、遇到的問題

關於ExpandableListview

在setOnGroupClickListener中修改group上TextView的文字時無效;
View child = ((ViewGroup)v).getChildAt(1);
((TextView)child).setText(“正在下載”)
這樣設置沒有效果,但是在setOnChildClickListener上做類似的修改是有效的,這裡在點擊group時,好像setOnGroupClickListener去調用了adapter的notifyDataSetChanged(),但是在程序中沒找到,難道這是默認行為?
最後還是通過下面的方法解決
clickMap.put(record.cityName, “1”);
adapter.notifyDataSetChanged();
通過修改數據,然後調用adapter.notifyDataSetChanged();在adapter中進行修改;

在item根部局上設置minHeight屬性可以有效的設置item的高度。

LayoutInflater類的infalter方法,幾種重載方法的區別:

之前對這個有所了解,這裡碰到的時候又忘記了,再次記錄下。
1. 如果root為null,attachToRoot將失去作用,設置任何值都沒有意義。
2. 如果root不為null,attachToRoot設為true,則會給加載的布局文件的指定一個父布局,即root。
3. 如果root不為null,attachToRoot設為false,則會將布局文件最外層的所有layout屬性進行設置,當該view被添加到父view當中時,這些layout屬性會自動生效。
4. 在不設置attachToRoot參數的情況下,如果root不為null,attachToRoot參數默認為true。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved